先看看这种核心流程图
这张图是SpringSecurity认证涉及到的核心类
让应用Debug启动
点击表单登录
进入到
这个就是上图中的绿色过滤器 , 这个类中首先进入attemptAuthentication这个方法,获取用户名密码,然后用获取到的用户名密码构建了一个UsernamePasswordAuthenticationToken这个对象
顶层就是实现了Authentication,这个Authentication实际上就是封装登录这的认证信息,接着上面构建UsernamePasswordAuthenticationToken对象就会调用下面的构造方法
这里会向父类传递一个null,
因为代码执行到这一步是还没有进行认证,所以这里还不知道用户权限,所以这里会向父类传递权限为null,回到UsernamePasswordAuthenticationToken类的public UsernamePasswordAuthenticationToken(Object principal, Object credentials) 构造方法
UsernamePasswordAuthenticationToken两个参的构造函数并设置认证状态为false
setAuthenticated(false);
这段代码时标识是否身份认证,上面讲到程序运行到这里是没有进行身份认证的,所以这里传入false,然后回到UsernamePasswordAuthenticationFilter类的attemptAuthentication方法
这个setDetails方法会向请求的一些信息放到UsernamePasswordAuthenticationToken,包括请求中机器的ip,session这些东西
在往下走
这一行代码就会进入到第二张流程图中的AuthenticationManager,AuthenticationManager自己并不包含验证的逻辑,他的作用是用来管理第二张图的AuthenticationProvider,向下走会进入这个类的
这个方法
这里循环中会拿到整个系统中所有实现AuthenticationProvider的实现类,这里上面提到的AuthenticationManager就是来收集并管理所有AuthenticationProvider的实现类,这里就会循环挨个去比对上面AuthenticationToken类型,也就是对比
return this.getAuthenticationManager().authenticate(authRequest);
这里找到DaoAuthenticationProvider
传入的这个UsernamePasswordAuthenticationToken,这里不同的Provider支持的Authentication类型是不一样的,根据传入的Authentication类型这里会挑出一个进行校验处理,回到上面的循环中
这里表单登录的活就会找到DaoAuthenticationProvider,
这里表单登录的实际校验逻辑是写在AbstractUserDetailsAuthenticationProvider类中的
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
这里调用了一个retrieveUser方法获取到一个user对象,这里的user就是UserDetails接口
retrieveUser这个方法是个抽象方法
这里在DaoAuthenticationProvider类中有这个方法的实现
loadedUser = this.getUserDetailsService().loadUserByUsername(username);
这行代码就再调用我们自己写的UserDetails接口的实现,这里就和SpringSecurity表单认证 这篇文章中自定义实现中的UserDetailsService类关联上了,UserDetailsService类就是在上面这句代码中调用的,调用的结果就是我们在UserDetailsService类中loadUserByUsername方法返回的结果,这里结果拿到后回到AbstractUserDetailsAuthenticationProvider类中的
如果没拿到会有一个异常抛出,拿到后会有一个
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
这里会做一个预检查,这里预检查也就是SpringSecurity表单认证这篇文章中提到的MyUserDetailsService中的第二个构造函数多4个构造参数的那个传入的用户状态
这里根据调用则来区分具体实现,那么这里调用者为preAuthenticationChecks
所以选
这里上面MyUserDetailsService中的第二个构造函数多4个构造参数传入的是4个,最后一个是在下面单独验证,往下继续
做完这个预检查后又会调用additionalAuthenticationChecks
这是个附加检查
这里又是一个抽象方法,这里又会回到DaoAuthenticationProvider中的实现,这个实现就是上面找到的DaoAuthenticationProvider,上面有做标记,全文搜索一下
密码校验
这里走完回到附加检查代码哪里,这里通过后往下走
会进入一个后检查,这里后检查就是相对预检查哪里的,因为预检查只检查了三个状态,那么这里既是检查第四个值代表的用户状态,那么这里具体的实现也看调用者,
那么走到这里所有的检查都通过了,那么就可以认为这个用户认证时成功的
那么这里认证成功后就会将获取到的用户信息user、authentication认证请求信息用createSuccessAuthentication来创建
这里createSuccessAuthentication就是从新来new UsernamePasswordAuthenticationToken,只是这里就不是调用两个参数的构造函数了,而是三个构造参数的函数这里可以全文搜索两个参的构造
UsernamePasswordAuthenticationToken两个参的构造函数并设置认证状态为false
实际上这里多的一个参数就是authorities
这里会调用父类super设置权限,并将认证状态改为true,标识认证成功,那么运行到这里就认证流程就已经走完了,那么此时一路return返回,就会回到
那么在往下走
实际上这里就是在调用我们自己写的登录成功后的处理器也就是第一部分上面写的自定义登录成功、失败处理逻辑,那么认证成功的逻辑就是这样的,那么在整个流程中任何一个步骤不过就会catch异常捕获
那么这里捕获的异常都会到我们自己的认证失败处理器上
我们的认证失败处理器会实现这个接口,重写这个方法。
那么到这里,我们上面的第二张流程图中的所有步骤就走完了,包括认证成功、失败、密码加密,那么都被串起来了,整个认证的源码分析流程就基本上完了!
FilterSecurityInterceptor