关键词:
doc:https://docs.spring.io/spring-security/site/docs/
基于表单的认证(个性化认证流程):
一、自定义登录页面
1、在securityConfigy配置类中的config方法中添加调用链方法
@Override protected void configure(HttpSecurity http) throws Exception http.formLogin()//指定是表单登录 .loginPage("/cus_login.html")//登录页面 .and() .authorizeRequests()//授权 .anyRequest()//任何请求 .authenticated();//都需要身份认证
2、同时在resources/resources下创建一个html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
自定义登录页面
</body>
</html>
/3、配置好之后,启动项目,访问localhost:9999/h api
浏览器后报错:,重定向到登录页面次数太多
4、为什么出现这种情况:
因为在securityconfigy配置类中指定了一个loginPage,但是在方法链中,有表明:任何请求都需要认证
.anyRequest()//任何请求 .authenticated();//都需要身份认证
处理:在anyRequest方法前和authorizeRequests方法后添加antMatchers匹配规则,指定登录页面允许访问
.authorizeRequests()//授权 .antMatchers("/cus_login.html").permitAll()
配置完成后再次访问localhost:9999/h :,即可跳转至自定义的登录页面
5、完善登录页面
//在usernamePasswordAuthenticationFilter中处理的action为/login,请求方式是post
public UsernamePasswordAuthenticationFilter() super(new AntPathRequestMatcher("/login", "POST"));
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> 自定义登录页面 <form action="/authentication/form" method="POST"> <table> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="text" name="password"></td> </tr> <tr> <td> <button type="submit">登录</button> </td> </tr> </table> </form> </body> </html>
因为在登录页面中设定的action为“/authentication/form”,所以在securityConfig配置类的方法链中添加loginProcessingUrl方法
@Override protected void configure(HttpSecurity http) throws Exception http.formLogin()//指定是表单登录 .loginPage("/cus_login.html")//登录页面 .loginProcessingUrl("/authentication/form") .and() .authorizeRequests()//授权 .antMatchers("/cus_login.html").permitAll() .anyRequest()//任何请求 .authenticated();//都需要身份认证
重启项目:访问localhost:9999/h
显示为自定义的页面。
csrf().disable();//跨站防护不适用
6、将loginPage(/cus_login.html) html页面改为一个controller代码
①新建一个controller
@RestController public class LoginSecurityController @RequestMapping("/authentication/require") public String requireAuthentication(HttpServletRequest request, HttpServletResponse response) return null;
②修改securityconfig配置类中的config中的方法调用链中的loginPage
@Override protected void configure(HttpSecurity http) throws Exception http.formLogin()//指定是表单登录 .loginPage("/authentication/require")//登录页面 .loginProcessingUrl("/authentication/form") .and() .authorizeRequests()//授权 .antMatchers("/authentication/require").permitAll() .anyRequest()//任何请求 .authenticated()//都需要身份认证 .and() .csrf().disable();//跨站防护不适用
二、自定义登录成功的处理
实现AuthenticationSucessHandler接口
三、自定义登录失败的处理
实现AuthenticationFailureHandler接口
四、源码学习
1、认证流程说明
登录请求进来是,会先到UsernamePasswordAuthentication类的,其实最先走的是它的父类AbstractAuthenticationProcessingFilter.java,在父类中会走attemptAuthentication方法,父类没有实现,因此会走子类的方法,当认证成功后,会走到最后successFulAuthentication方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 。。。。
。。。。 Authentication authResult; try authResult = attemptAuthentication(request, response); if (authResult == null) // return immediately as subclass has indicated that it hasn‘t completed // authentication return; sessionStrategy.onAuthentication(authResult, request, response); 。。。。 。。。。 // Authentication success if (continueChainBeforeSuccessfulAuthentication) chain.doFilter(request, response); successfulAuthentication(request, response, chain, authResult);
attemptAuthentication方法(子类usernamepasswordAuthenticationFilter.java)
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException if (postOnly && !request.getMethod().equals("POST")) throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); String username = obtainUsername(request); String password = obtainPassword(request); 。。。。
。。。。
username = username.trim(); //这个UsernamepasswordAuthenticationToken其实就是封装了username 和password信息 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // 这个setDetails会把请求的一些信息设置到authRequest对象中 setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest);
上边那个this.getAuthenticationManager() 会获取一个authenticationManager类
作用:收集相关的provider,当请求到的时候,循环判断是否支持当前的provider类型
public interface AuthenticationManager
//authenticate认证方法,交给实现类实现 Authentication authenticate(Authentication var1) throws AuthenticationException;
他的实现类(用的就是ProviderManger类),不同的provider支持的Authentication是不同的
例如:UsernamePasswordAuthenticationToken类型的Authentication时,provider就是DaoAuthenticationProvider,
public Authentication authenticate(Authentication authentication) throws AuthenticationException Class<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; AuthenticationException parentException = null; Authentication result = null; Authentication parentResult = null; boolean debug = logger.isDebugEnabled(); Iterator var8 = this.getProviders().iterator(); while(var8.hasNext()) AuthenticationProvider provider = (AuthenticationProvider)var8.next(); if (provider.supports(toTest)) if (debug) logger.debug("Authentication attempt using " + provider.getClass().getName()); try result = provider.authenticate(authentication); if (result != null) this.copyDetails(authentication, result); break; catch (AccountStatusException var13) this.prepareException(var13, authentication); throw var13; catch (InternalAuthenticationServiceException var14) this.prepareException(var14, authentication); throw var14; catch (AuthenticationException var15) lastException = var15; 。。。。。
上边那个标红的provider.authenticate方法会走到DaoAuthenticationProvider对象的父类对象的authticate方法,
public Authentication authenticate(Authentication authentication) throws AuthenticationException Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"); ); String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName(); boolean cacheWasUsed = true; UserDetails user = this.userCache.getUserFromCache(username); if (user == null) cacheWasUsed = false; try
//这块会进入子类DaoAuthenticationPrivoder user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication); catch (UsernameNotFoundException var6) this.logger.debug("User ‘" + username + "‘ not found"); if (this.hideUserNotFoundExceptions) throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); throw var6; Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract"); try
//校验是否锁定。。(userdetails里边的几个返回值) this.preAuthenticationChecks.check(user);
//校验密码是否匹配 this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); catch (AuthenticationException var7) if (!cacheWasUsed) throw var7; cacheWasUsed = false; user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication)
this.preAuthenticationChecks.check(user); this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication); //后置查询 this.postAuthenticationChecks.check(user); if (!cacheWasUsed) this.userCache.putUserInCache(user); Object principalToReturn = user; if (this.forcePrincipalAsString) principalToReturn = user.getUsername(); return this.createSuccessAuthentication(principalToReturn, authentication, user);
子类DapAuthenticationPrivoder类的
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException this.prepareTimingAttackProtection(); try
//这一块就是调用自定义得人UserDetailsService类 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); if (loadedUser == null) throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation"); else return loadedUser; catch (UsernameNotFoundException var4) this.mitigateAgainstTimingAttack(authentication); throw var4; catch (InternalAuthenticationServiceException var5) throw var5; catch (Exception var6) throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
进入自定义的CusUserDetailsService
public class MyUserDetailsService implements UserDetailsService @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException //根据username查找用户信息,在这,先手动写一个user信息 log.info("查找用户信息", s); BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String password = encoder.encode("password"); //user前两个参数是进行认证的,第三个参数是当前用户所拥有的权限,security回根据授权代码进行验证 return new User(s, password, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); // AuthorityUtils.commaSeparatedStringToAuthorityList 这个方法是将一个字符一“,” 分割开
二初始化springsecurity(代码片段)
一、初始化SpringSecurity1.1、SpringSecurity概念SpringSecurity是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。1.2、SpringSecurity简单入门SpringSecurit... 查看详情
二初始化springsecurity(代码片段)
一、初始化SpringSecurity1.1、SpringSecurity概念SpringSecurity是spring采用AOP思想,基于servlet过滤器实现的安全框架。它提供了完善的认证机制和方法级的授权功能。是一款非常优秀的权限管理框架。1.2、SpringSecurity简单入门SpringSecurit... 查看详情
二springsecurity基本原理(代码片段)
一、SpringSecurity常见过滤器介绍SpringSecurity本质是一个过滤器链从启动时,可以获取到过滤器链:SpringSecurity中过滤器的介绍:org.springframework.security.web.context.SecurityContextPersistenceFilter首当其冲的一个过滤器,作用之... 查看详情
springsecurity学习(代码片段)
1.SpringSecurity框架简介1.1概要Spring是非常流行和成功的Java应用开发框架,SpringSecurity正是Spring家族中的成员。SpringSecurity基于Spring框架,提供了一套Web应用安全性的完整解决方案。正如你可能知道的关于安全方面的两个主要区域是... 查看详情
springsecurity学习——快速开始(代码片段)
前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情
springsecurity学习——快速开始(代码片段)
前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情
springboot集成springsecurity——入门程序(代码片段)
因为项目需要,第一次接触SpringSecurity,早就听闻SpringSecurity功能强大但上手困难,学习了几天出入门道,特整理这篇文章希望能让后来者少踩一点坑(本文附带实例程序,请放心食用)本篇文章环境:SpringBoot2.0+Mybatis+SpringSecurit... 查看详情
springsecurity学习总结(代码片段)
这几天一直在学习springsecurity的相关知识.逛各大论坛,看相关api与教学视频,获益良多!简介SpringSecurity是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了完整的安全性解决方案,可以在Web... 查看详情
springsecurity学习——密码加密(代码片段)
前言上一篇文章我们实现了从数据库读取用户名密码到SpringSecurity中,并验证登录成功。不过密码的形式有点奇怪,这篇文章我们研究一下密码加密和比对的问题。SpringSecurity的密码加密和比对密码编码器的使用SpringSecurit... 查看详情
安全框架springsecurity是什么?如何理解springsecurity的权限管理?(代码片段)
大家好,我是卷心菜。本篇主要讲解SpringSecurity的基本介绍和架构分析,如果您看完文章有所收获,可以三连支持博主哦~,嘻嘻。文章目录一、前言二、SpringSecurity简介三、权限管理1、什么是权限管理2、什么是认... 查看详情
springsecurity学习(代码片段)
认证和授权认证解决“我是谁的问题”什么是认证?认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪一个用户授权解决“我能做什么”的问题授权:经过认证之后判断当前用户是否有权限进... 查看详情
springsecurity-用户动态授权及动态角色权限(代码片段)
一、SpringSecurity动态授权上篇文章我们介绍了SpringSecurity的动态认证,上篇文章就说了SpringSecurity的两大主要功能就是认证和授权,既然认证以及学习了,那本篇文章一起学习了SpringSecurity的动态授权。上篇文章地址... 查看详情
java之springboot+springsecurity+vue实现后台管理系统的开发二后端(代码片段)
Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【一、前端】跳转Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【二、后端】跳转Java之SpringBoot+SpringSecurity+Vue实现后台管理系统的开发【三、系统权限... 查看详情
springsecurity源码:整体框架设计(代码片段)
...瞅三、框架接口设计建造者配置器四、总结五、系列文章SpringSecurity系列SpringSecurityOAuth系列一、一句话概括框架原理整个框架的核心就是构建一个名字为springSecurityFilterChain的过滤器Bean,它的类型是FilterChainProxy。二、何处开... 查看详情
springsecurity学习——快速开始(代码片段)
前言SpringSecurity是目前Java后台管理系统中使用最常见的安全认证框架之一。它的上手没Shiro那么直观。Shiro是可以直接开个javaSE应用验证的,但SpringSecurity基本要求和web关联起来。刚开始看甚至不知道从何入手。最近决定静下... 查看详情
springsecurity学习——使用数据库保存密码(代码片段)
前言上一篇文章我们已经快速搭起SpringSecurity应用。我们使用的是默认的用户名密码。通常项目中我们使用数据库保存密码,并且数据库的密码是加密保存的。如何做到呢,是这篇文章的重点。引入数据库新建数据库表我... 查看详情
springsecurity-用户动态授权及动态角色权限(代码片段)
一、SpringSecurity动态授权上篇文章我们介绍了SpringSecurity的动态认证,上篇文章就说了SpringSecurity的两大主要功能就是认证和授权,既然认证以及学习了,那本篇文章一起学习了SpringSecurity的动态授权。上篇文章地址... 查看详情
轻松上手springsecurity,oauth,jwt(代码片段)
目录学习目标一.SpringSecurity1.SpringSecurity简介及快速入门<1>.SpringSecurity简介<2>.SpringSecurity快速入门2.SpringSecurity基本原理<1>.UserDetailsService详解<2>.PasswordEncoder密码解析器详解3.SpringSecurity自定义 查看详情