1. 认证源码剖析
1.1 doFilter方法
- AbstractAuthenticationProcessingFilter进入doFilter,判断提交方式是不是post请求,如果是,则进行下一步的认证,否则放行则进入下一个过滤器。
- 调用子类的attemptAuthentication方法获取表单的数据进行验证,返回Authentication。
Authentication是用来存储用户认证信息的类
- Session策略处理(如果配置了用户Session最大并发数,就是在此处进行判断并处理
- 认证成功,调用认证成功的方法successfulAuthentication
认证失败,调用认证失败的方法unsuccessfulAuthentication
1.2 attemptAuthentication方法
上述的 第二 过程调用了 UsernamePasswordAuthenticationFilter 的attemptAuthentication() 方法,源码如下:
UsernamePasswordAuthenticationFilter:子类
attemptAuthentication:方法
完整的attemptAuthentication方法:
(1)默认情况下,如果请求方式不是Post,会抛出异常。
(2)获取请求携带的usernane和password
(3)使用前端传入的usernane、password构造Authentication对象,标记该对象未认证。
(4)将请求中的一些属性信息设置到Authentication对象中,如:remoteAddress,sessionId
(5)调用ProviderManager类的authenticate()方法进行身份认证
1.3 UsernamePasswordAuthenticationToken类
上述的1.2 attemptAuthentication方法(3)过程创建的 UsernamePasswordAuthenticationToken 是Authentication 接口的实现类,该类有两个构造器,一个用于封装前端请求传入的未认证的用户信息,一个用于封装认证成功后的用户信息:
用于封装前端请求传入的未认证的用户信息,前面的 authRequest对象就是调用该杓造器进行构造的
用封装认证成功后的用户信息
1.4 Authentication接口
Authentication 接口的实现类用于存储用户认证信息,查看该接口具体定义
1.5 authenticate方法
上述1.2 attemptAuthentication方法(6)过程中,UsernamePasswordAuthenticationFilter 过滤器的attemptAuthentication() 方法的(5)过程将未认证的 Authentication 对象传入ProviderManager 类的 authenticate() 方法进行身份认证
ProviderManager 是 AuthenticationManager 接口的实现类
在该接口的常用实现类 ProviderManager 内部会维护一个List<AuthenticationProvider>
列表,存放多种认证方式,实际上这是委托者模式(Delegate)的应用。每种认证方式对应着一个 AuthenticationProvider,AuthenticationManager 根据认证方式的不同(根据传入的 Authentication 类型判断)委托对应的 AuthenticationProvider 进行用户认证。
完整的authenticate代码:
源码说明:
1.6 eraseCredentials方法
上述1.5 authenticate方法认证成功之后的(6)过程,调用 CredentialsContainer 接口定义的eraseCredentials() 方法去除敏感信息。查看UsernamePasswordAuthenticationToken 实现的 eraseCredentials() 方法,该方法实现在其父类中:
1.7 认证成功/失败处理
上述过程就是认证流程的最核心部分,接下来重新回到UsernamePasswordAuthenticationFilter 过滤器的 doFilter() 方法,查看认证成功/失败的处理:
2. 授权源码剖析
上一个部分通过源码的方式介绍了认证流程,下面介绍权限访问流程,主要是对ExceptionTranslationFilter过滤器和 FilterSecurityInterceptor 过滤器进行介绍。
2.1 ExceptionTranslationFilter 过滤器
该过滤器是用于处理异常的,不需要我们配置,对于前端提交的请求会直接放行,捕获后续抛出的异常并进行处理(例如:权限访问限制)。具体源码如下:
2.2 FilterSecurityInterceptor 过滤器
FilterSecurityInterceptor 是过滤器链的最后一个过滤器,该过滤器是过滤器链的最后一个过滤器,根据资源权限配置来判断当前请求是否有权限访问对应的资源。如果访问受限会抛出相关异常,最终所抛出的异常会由前一个过滤器ExceptionTranslationFilter 进行捕获和处理。具体源码如下:
需要注意,Spring Security 的过滤器链是配置在 SpringMVC 的核心组件DispatcherServlet 运行之前。也就是说,请求通过 Spring Security 的所有过滤器,不意味着能够正常访问资源,该请求还需要通过 SpringMVC 的拦截器链。
2.3 SpringSecurity 请求间共享认证信息
一般认证成功后的用户信息是通过 Session
在多个请求之间共享,那么 Spring Security 中是如何实现将已认证的用户信息对象 Authentication 与 Session 绑定的进行具体分析。
在前面讲解认证成功的处理方法 successfulAuthentication() 时,有以下代码:
2.4 SecurityContext 接 口
查 看 SecurityContext 接 口 及 其 实 现 类 SecurityContextImpl , 该 类 其 实 就 是 对 Authentication 的封装:
2.5 SecurityContextHolder类
查 看 SecurityContextHolder 类 , 该 类 其 实 是 对 ThreadLocal 的 封 装 , 存 储 SecurityContext 对象:
SecurityContextHolder
底层使用了ThreadLocal
使用ThreadLocal 对Securitycontext进行存储
需要汪意,如果当前线程对应的ThreadLocal<SecurityContext>
没有任何对象存储, ! getcontext()会创建并返回一个空的SecurityContext对象,并且该空的SecurityContext 对象会存入ThreadLocal<SecurityContext>
2.6 SecurityContextPersistenceFilter 过滤器
前面提到过,在 UsernamePasswordAuthenticationFilter 过滤器认证成功之后,会在认证成功的处理方法中将已认证的用户信息对象 Authentication 封装进SecurityContext,并存入 SecurityContextHolder。
之后,响应会通过 SecurityContextPersistenceFilter 过滤器,该过滤器的位置在所有过滤器的最前面,请求到来先进它,响应返回最后一个通过它,所以在该过滤器中处理已认证的用户信息对象 Authentication 与 Session 绑定。
认证成功的响应通过 SecurityContextPersistenceFilter 过滤器时,会从SecurityContextHolder 中取出封装了已认证用户信息对象 Authentication 的SecurityContext,放进 Session 中。当请求再次到来时,请求首先经过该过滤器,该过滤器会判断当前请求的 Session 是否存有 SecurityContext 对象,如果有则将该对象取出再次放入 SecurityContextHolder 中,之后该请求所在的线程获得认证用户信息,后续的资源访问不需要进行身份认证;当响应再次返回时,该过滤器同样从 SecurityContextHolder 取出SecurityContext 对象,放入 Session 中。具体源码如下:
(1)请求到来时,检查当前Session中是否存有SecurityContext对象,如果有,从Sess1on中取出该对象;如果没有,创建一个空的SecurityContext 对象
(2)将上述获得SecuritvContext 对象放入securitvcontextHolder中
(3)进入下一个过滤器
(4)响应返回时,从SecurityContextHolder中取出SecurityContext
(5) 移除SecuritycontextHolder中的Securitycontext对象
(6)将取出的SecurityContext对象放进session
内容参考:https://www.bilibili.com/video/BV15a411A7kP
仅用于学习!