SpringSecurity流程

487 阅读6分钟

SpringSecurity详细流程

Spring Security采用的是责任链的设计模式,由一组很长的过滤器链组成,我们先来看看其详细的流程图,然后详细分析每个过滤器在流程图中所处位置扮演了什么样的角色,发挥了什么样的作用。

1. SpringSecurity详细流程图:

未命名文件.png

2. SpringSecurity常用过滤器

2.1 WebAsyncManagerIntegrationFilter

Web异步管理集成过滤器。此过滤器使得WebAsync异步线程能够获取到当前认证信息。用于集成SecurityContext到Spring异步执行机制中的WebAsyncManager。用来处理异步请求的安全上下文。

WebAsyncManager是一个异步请求管理器,主要是针对异步请求如何能和同步请求一样对一些数据进行处理。 WebAsyncManagerIntegrationFilter就是用来管理异步请求中SecuurityContext的生命周期的,主要是通过SecurityContextCallableProcessingInterceptor来持有SecurityContext,然后注入到SecurityContextHolder(SecurityContextHolder是其他组件获取SecurityContext的公共类),再到最后清理SecurityContext

具体逻辑:

  • 从请求属性上获取所绑定WebAsyncManager,如果尚未绑定则先做绑定
  • 从asyncManager中获取key为CALLABLE_INTERCEPTOR_KEY的安全上下文多线程处理器SecurityContextCallableProcessingInterceptor,如果获取到为null,则新建一个并绑定CALLABLE_INTERCEPTOR_KEY注册到asyncManager中。

SecurityContextCallableProcessingInterceptor实现了接口CallableProcessingInterceptor,当他被应用于一次异步请求时,beforeConcurrentHandling()方法会在调用者线程执行,该方法会相应的从当前线程获取SecurityContext,然后 被调用者线程中执行逻辑时,会使用这个SecurityContext,从而实现安全上下文从调用者线程到被调用者线程的传输。

2.2 SecurityContextPersistenceFilter

SecurityContextPersistenceFilter主要有两个任务:

  1. 在请求到达时处理前,从SecurityContextRepository中获取安全上下文信息填充到SecurityContextHolder
  2. 在请求处理结束后返回响应时,将SecurityContextHolder中的安全上下文信息保存回SecurityContextRepository,并清空SecurityContextHolder。

工作流程:

对于一些跨request保持的场景,通常时通过session来保存request中的信息,当下次请求过来时,可以通过session获取之前请求中的信息。这个Filter就是处理请求最后的保存操作

  1. 一个安全上下文在请求1处理过程中被创建并保存到SecurityContextHolder中

  2. 请求1处理结束时,会将SecurityContextHolder中的安全上下文信息保存到HttpSession(SecurityContextRepository负责安全上下文的持久化)

  3. 后续该用户会话中的另一个请求2处理过程开始时,Filter会将安全上下文从HttpSession中恢复到SecurityContextHolder

  4. 请求2处理结束时,Filter会将SecurityContextHolder中的安全上下文保存到HttpSession

  5. 后续其他请求重复重置/恢复SecurityContext的动作

2.3 HeaderWriterFilter

往请求头或响应头中写入一些信息。比如 X-Frame-Options, X-XSS-Protection ,X-Content-Type-Options。

通过shouldWriteHeadersEagerly控制写入时机,true时写操作在过滤链执行前,否则过滤链执行完毕后再进行写入

2.4 CsrfFilter

用于处理跨站请求伪造,防止CSRF攻击。

2.5 LogoutFilter

用于处理退出登录,可配置自定义的登出逻辑。

2.6 UserNamePasswordAuthenticationFilter

用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自“/login”的请求,从表单中获取用户名和密码时,默认使用表单name值为username和password,这个值可以通过设置这个过滤器的usernameParameter和passwordParameter两个参数进行修改。

提交的username和password会被封装为未经认证的AuthenticationToken进行一系列的认证

2.7 DefaultLoginPageGeneratingFilter

如果没有配置登录页面,那么系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面

2.8 BasicAuthenticationFilter

检测和处理http basic认证

2.9 RequestCacheAwareFilter

请求缓存过滤器,主要作用是认证完成后恢复认证前的请求继续执行

用户认证成功后,重新恢复因登录被打断的请求。当匿名访问 一个需要授权的资源时,会跳转到认证处理逻辑,此时请求被缓存。在认证逻辑处理完成后,从缓存中获取最开始的资源请求进行再次请求。

2.10 SecurityContextHolderAwareRequestFilter

主要是包装请求对象request

目的是实现servlet api的一些接口方法:isUserRole、getRemoteUser。在springSecurity中就是通过这个过滤器实现的

2.11 AnonymousAuthenticationFilter

匿名认证过滤器。对于SpringSecurity来说,所有资源的访问都是有Authentication的,对于无需登录认证即可直接访问的资源会授予匿名用户身份

检测SecurityContextHolder中是否存在Authentication对象,如果不存在则为其提供一个匿名Authentication

2.12 SessionManagementFilter

管理session的过滤器。内部维护了一个SessionAuthenticationStrategy用于管理session

2.13 ExceptionTranslationFilter

处理AccessDecisionException和AuthenticationException异常。

2.14 FilterSecurityInterceptor

请求鉴权过滤器,这个过滤器决定了访问特定路径应该具备的权限,访问的用户的角色,权限是什么,访问的路径需要什么样的角色和权限。这些判断和处理都由该过滤器完成

2.15 RemembeMeAuthenticationFilter

处理“记住我”功能的过滤器。当用户没有登录而直接访问资源时,从cookie里找出用户的信息,如果springsecurity能够识别用户提供的remember me cookie,用户将不必填写用户名和密码,而是直接登录进入系统,该过滤器默认不开启

Spring Security核心流程总结

一系列的过滤器处理逻辑仍旧十分复杂,有一些时默认开启,有一些则需要进行相应的配置才会生效,专注于重点功能逻辑,梳理一下Spring Security的访问流程:

是否是登出->是否是登录->权限校验

  1. 用户发起请求,进入过滤器链后,到达LogoutFilter时,会先判断是否时登出请求

    • 登出请求: 则通过LogoutHandler处理登出逻辑
      • 登出成功: 进入LogoutSuccessHandler执行登出成功后的处理
      • 登出失败: 进入LogoutFailureHandler执行登出失败后的处理
    • 不是登出请求: 则检查是否配置了登录页,若没有配置则会通过DefaultLoginPageGeneratingFilter生成默认的登录页
  2. 判断是否为登录请求,

    • 是登录请求: 进入UserNamePasswordAuthenticationFilter过滤器,进行Authentication认证处理
    • 不是登录请求: 则进入下一个过滤器。
  3. 到达FilterSecurityInterceptor,鉴权过滤器,进行权限校验处理,判断当前用户是否拥有当前请求路径的访问权限

    • 拥有访问权限:进入controller执行代码逻辑
    • 无访问权限: 进入AccessDeniedHandler执行鉴权失败逻辑。