了解SpringSecurity更多功能前,咱们先了解下其认证流程源码 前端
咱们在前面几节中说了SpringSecurity的个性化用户认证流程:
自定义用户认证逻辑: spring
咱们上面都是去实现Spring给咱们的接口,好比自定义用户认证逻辑:
实现UserDetails、UserDetailsService、接口;
在个性化用户认证流程时候实现SpringSecurity自带的:登陆失败实现:AuthenticationFailureHandler;登陆成功实现:
AuthenticationSuccessHandler接口。
目前头脑里就是碎片化的登陆认证流程,只知道登陆成功怎么作,登陆失败后如何作?可是在Spring里面是怎样把全部逻辑串起来的?浏览器
认证流程源码级详解主要讲解如下3点:微信
咱们以表单认证为例:从发起认证请求到认证过滤器,接着认证成功后,响应从认证过滤器返回的整个过程。SpringSecurity作了什么,设计到了哪些类?他们之间如何调用?
SpringSecurity认证流程中涉及到的主要的类和接口以下:session
咱们按照上面图示剖析源码: app
应用debug启动,浏览器登陆: ide
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);函数
super((Collection)null);//调用父类构造函数; ui
AuthenticationManager:自身并不包含验证逻辑,做用用来管理下面的AuthenticationProvider;AuthenticationManager下面有不少实现类,最后是在
ProviderManager: this
ProviderManager在public Authentication authenticate(Authentication authentication)会进行for循环,获取全部AuthenticationProvider,全部校验逻辑是在AuthenticationProvider里面的,为何这里是一个集合:是由于不一样的登陆方式其认证逻辑是不同的,咱们如今是用户名密码登陆,是须要去校验密码。若是是微信登陆,则又是不同的。AuthenticationManager做用就是把全部AuthenticationProvider搜集起来,认证时候,挨个去问,你当前的provider支不支持我如今的的登陆方式(其实就是作循环,而后调用supports方法)。
而后进入DaoAuthenticationProvider,DaoAuthenticationProvider会调用其父类的AbstractUserDetailsAuthenticationProvider的
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
authenticate方法
这里和咱们自定义认证逻辑上符合了,咱们自定义认证逻辑时候就是实现了一个UserDetailsService接口。
预检查---->检查UserDetails的3个boolean:
附加检查:在DaoAuthenticationProvider--->检查密码是否匹配
而后进行后检查:后检查主要检查4个boolean中最后一个:
若是上面检验所有没问题的话,就认为认证是合法的,而后咱们再建立一个已经受权的Authentication
最后就会按照下图链返回:一直到:UsernamePasswordAuthenticationFilter
再往下: AbstractAuthenticationProcessingFilter
登陆成功以后走到咱们自定义的成功处理器:
SecurityContextHolder.getContext().setAuthentication(authResult);
自定义成功处理器:
登陆成功后:把认证信息打印出去:
response.getWriter().write(objectMapper.writeValueAsString(authentication));
try-catch补货时候,若是有异常抛出就会执行:
多个请求共享确定是放到session里,那么SpringSecurity是何时?什么东西放到了session里面?何时又从session里面读取出来的?
在这个过程当中涉及到以下右边类:
AbstractAuthenticationProcessingFilter里面successfulAuthentication有一个:
SecurityContextHolder.getContext().setAuthentication(authResult);
实际上是把咱们认证成功的Authentication放到咱们的: SecurityContext里面,而后SecurityContext放到SecurityContextHolder里面。
SecurityContext其实很简单,他就是一个接口,其惟一的实现类是:
package org.springframework.security.core.context; import org.springframework.security.core.Authentication; public class SecurityContextImpl implements SecurityContext { private static final long serialVersionUID = 420L; private Authentication authentication; public SecurityContextImpl() { } public boolean equals(Object obj) { if (obj instanceof SecurityContextImpl) { SecurityContextImpl test = (SecurityContextImpl)obj; if (this.getAuthentication() == null && test.getAuthentication() == null) { return true; } if (this.getAuthentication() != null && test.getAuthentication() != null && this.getAuthentication().equals(test.getAuthentication())) { return true; } } return false; } public Authentication getAuthentication() { return this.authentication; } public int hashCode() { return this.authentication == null ? -1 : this.authentication.hashCode(); } public void setAuthentication(Authentication authentication) { this.authentication = authentication; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append(super.toString()); if (this.authentication == null) { sb.append(": Null authentication"); } else { sb.append(": Authentication: ").append(this.authentication); } return sb.toString(); } }
SecurityContext说明:咱们看SecurityContextImpl源码知道,其实他就是对Authentication的包装,而且重写其hashCode和equals,保证其惟一性。
SecurityContextHolder:他其实是ThreaadLocal封装。ThreaadLocal是跟线程绑定的一个Map,在这个线程里面存放的东西能够在另外一个线程读取出来,你能够理解为:线程的全局变量。
最后:SecurityContextHolder会交给SecurityContextPersistenceFilter过滤器:他的位置在过滤器链的最前端:
SecurityContextPersistenceFilter过滤器做用:
这样不一样的请求能够从线程里面拿到认证信息。拿到之后放到线程里面,由于请求和响应是在一个线程中。
咱们如何用SecurityContext获取用户信息。咱们在Spring-Security-demo 里面的controller里面获取用户信息
浏览器访问:
咱们也能够经过SpringMvc本身作的注入查找,咱们修改以下:
也能够要求放回用户详情信息,其余信息不要了。