Spring Security表单认证流程分析(四)

Spring Security过滤器

HttpSecurity.java#performBuild 获取 DefaultSecurityFilterChain

http://xhope.top/wp-content/uploads/2021/10/s1.png

0 = {WebAsyncManagerIntegrationFilter@5192}
这个过滤器用于集成SecurityContext到Spring异步执行机制的WebAsyncManager 中。如果想要与spring集成,就必须要使用此过滤器链。

1 = {SecurityContextPersistenceFilter@6142} 
主要是使用SecurityContextRepository在session中保存或更新一个SecurityContext域对象(相当于一个容器),并将SecurityContext给以后的过滤器使用,来为后续filter建立所需的上下文。SecurityContext中存储了当前用户的认证以及权限信息。 其他的过滤器都需要依赖于它。在 Spring Security 中,虽然安全上下文信息被存储于 Session 中,但我们在实际使用中不应该直接操作 Session,而应当使用 SecurityContextHolder。

2 = {HeaderWriterFilter@6125} 
用于将头信息加入响应中。

3 = {LogoutFilter@6163}
匹配URL为/logout的请求,实现用户退出,清除认证信息。

4 = {UsernamePasswordAuthenticationFilter@6199}
认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。

5 = {BasicAuthenticationFilter@6250} 
此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。

6 = {RequestCacheAwareFilter@6146} 
通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest。

7 = {SecurityContextHolderAwareRequestFilter@6156}
针对ServletRequest进行了一次包装,使得request具有更加丰富的API。

8 = {AnonymousAuthenticationFilter@6150} 
当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。
spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份(游客)

9 = {SessionManagementFilter@6137} 
SecurityContextRepository限制同一用户开启多个会话的数量

10 = {ExceptionTranslationFilter@6113} 
异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常

11 = {FilterSecurityInterceptor@6194}
获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其是否有权限

认证流程

POST /login

  1. SecurityContextPersistenceFilter:从SecurityContextRepository获取认证信息,放入上下文中。如果未认证,createEmptyContext。
  2. UsernamePasswordAuthenticationFilter:
    • 是否路径是/login,请求是POST
    • 获取用户名密码,包装成对象 UsernamePasswordAuthenticationTokenProviderManager 进行验证
    • ProviderManager 使用 DaoAuthenticationProvider 进行处理。因为它supports UsernamePasswordAuthenticationToken。
    • 根据username获取UserDetails
    • 检查账号有没有锁定,过期,可用等
    • 检查用户名密码是否匹配
    • 检查密码是否过期
    • 验证成功后,返回成功的Authentication。放到SecurityContextHolder上下文中。记住我,successHandler。
    • 验证失败后,AbstractAuthenticationProcessingFilter捕获异常,处理failureHandler
  3. AnonymousAuthenticationFilter:上下文中没有认证信息,创建匿名认证
  4. FilterSecurityInterceptor: this.accessDecisionManager.decide(authenticated, object, attributes) 权限进行投票(通过spring-expression表达式去判断)。
    默认投票实现 AffirmativeBased 的逻辑:

    a.只要有AccessDecisionVoter的投票为ACCESS_GRANTED则同意用户进行访问;

    b.如果全部弃权也表示通过;

    c.如果没有一个人投赞成票,但是有人投反对票,则将抛出AccessDeniedException。

POST /public

  1. SecurityContextPersistenceFilter:从SecurityContextRepository获取认证信息,放入上下文中。如果未认证,createEmptyContext。
  2. AnonymousAuthenticationFilter
  3. FilterSecurityInterceptor: 进行投票

POST /index

  1. SecurityContextPersistenceFilter:从SecurityContextRepository获取认证信息,放入上下文中。如果未认证,createEmptyContext。
  2. AnonymousAuthenticationFilter
  3. FilterSecurityInterceptor: 进行投票

POST /admin

  1. SecurityContextPersistenceFilter:从SecurityContextRepository获取认证信息,放入上下文中。如果未认证,createEmptyContext。
  2. AnonymousAuthenticationFilter
  3. FilterSecurityInterceptor: 进行投票

参考: