本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12。

Spring Security 的本质是一个过滤器链(filter chain),当一个请求(request)访问 Web 应用提供的资源时,首先要经过一系列过滤器(filter)的处理,根据过滤器处理的结果返回不同的信息,包括:

  • 返回用户请求的资源
  • 请求未认证,需要认证

    包括请求未认证和之前的认证已过期,一般会重定向到一个登录页面让用户认证。

  • 已认证请求没有权限访问资源

Spring Web 中的过滤器链

用户请求进入 Spring Web 的入口是 ApplicationFilterChain(org.apache.catalina.core 包)的 doFilter() 方法,在这条过滤器链上(按执行顺序)有下述过滤器。这些过滤器在 Tomcat 的 web.xml 文件中定义,并在 Web 服务器启动时初始化。

  • OrderedCharacterEncodingFilter

    继承自 CharacterEncodingFilter,实现 OrderedFilter 接口来指定过滤器执行的顺序。

    用于设置请求(和响应)的编码。

  • OrderedHiddenHttpMethodFilter

    本过滤器会将请求的方法参数(method parameters)转换为 HTTP method,使得可通过 HttpServletRequest 的 getMethod() 方法获取。

    由于浏览器目前仅支持 GET 和 POST 请求,一个常用的技术是使用一个 POST 请求再加上一些隐藏的表单字段(hidden form field)来表示 PUT、DELETE 和 PATCH 请求。

    由于需要检查 POST body 参数,因此在 multipart POST 请求时,本过滤器需要在 multipart 处理之后运行。

    具体的做法是在过滤器链中将 MultipartFilter 放在 HiddenHttpMethodFilter 前面。

  • OrderedFormContentFilter

    为 PUT、PATCH 以及 DELETE 请求解析表单数据(form data),并将解析结果暴露(exposes)为 Servlet request 参数。

    Servlet 规范默认只有 POST 请求会使用本过滤器。

  • OrderedRequestContextFilter

    通过 LocaleContextHolder 和 RequestContextHolder 将当前 request 暴露给当前线程。

    RequestContextListener 和 DispatcherServlet 也会将相同的 request 上下文暴露给当前线程。

    本过滤器主要用于第三方 Servlets,Spring 自己的 DispatcherServlet 处理效率很高,不需要使用到它。

  • DelegatingFilterProxy(DelegatingFilterProxyRegistrationBean$1)

    DelegatingFilterProxyRegistrationBean 在 Web 应用启动时通过 getFilter() 将 DelegatingFilterProxy 注册到 Servlet 容器。

    DelegatingFilterProxy 是 Servlet 过滤器的代理,它将过滤器委托给实现了 Filter 接口的 Spring Bean。

    web.xml 文件中通常会包含一个指定 filter-name 的 DelegatingFilterProxy 定义,这个 filter-name 即是 Spring 容器中的 bean-name。

    对过滤器代理的调用会被委托给 Spring 容器中的 bean。

  • SsoSecurityInterceptorImpl

    用户自定义实现的过滤器。

  • WsFilter

    用于处理 WebSocket 连接时的初始 HTTP 连接,位于 org.apache.tomcat.websocket.server 包。

Spring Security 中的过滤器链

Spring Security 对请求的处理从 DelegatingFilterProxy 的 doFilter() 方法开始,并包括用户自定义的过滤器(若存在)。DelegatingFilterProxy 在 doFilter() 中通过 FilterChainProxy 定义的过滤器链来处理请求。

FilterChainProxy 中过滤器链上的 filter 都在 org.springframework.security 包中(除去用户自定义的过滤器)。

FilterChainProxy 负责将 Filter 请求分发(delegates)到 Spring 容器的 filter beans 列表,由列表中的 filter bean 对请求进行认证和授权相关的处理。

通过在 web.xml 文件中添加 DelegatingFilterProxy 的声明,FilterChainProxy 被连接到了 servlet 容器的过滤器链中。

FilterChainProxy 中包含下述过滤器(按执行顺序):

  • WebAsyncManagerIntegrationFilter

    提供 SecurityContext 和 WebAsyncManager 的集成,实现对请求处理的异步管理。

  • SecurityContextPersistenceFilter

    本过滤器是容器 session 和 Spring Security 之间的桥梁,它应该在任何认证处理机制(authentication processing mechanisms)之前执行,因为 authentication 需要一个可用的 SecurityContext。

    它会从 session 中取出 key 为 SPRING_SECURITY_CONTEXT 的 attribute,这是一个 SecurityContextImpl 实例。

    若请求在之前已认证,那么 session 会关联上相关信息。若没有,那么通常会经过 AnonymousAuthenticationFilter,此时其 Authentication 值是 AnonymousAuthenticationToken。

    由于请求是单独的线程,而安全认证是基于 session 的,所以请求需要将认证信息从 session 中取出,认证结束后再复制回 session(因为认证信息可能会发送改变) 以供下一个请求使用。

    在 AbstractAuthenticationProcessingFilter 的 successfulAuthentication() 中,认证成功后会将 Authentication 写到 SecurityContext 中。

    在 doFilter() 方法中会获取 request 的 __spring_security_scpf_applied 属性,若存在该属性则把请求传递给下一个过滤器。否则设置该属性,并继续执行 doFilter() 方法。

    判断 forceEagerSessionCreation 属性,查看是新创建了 session。

    获取一个 SecurityContext,在执行过滤器链前把它放入 SecurityContextHolder 中,完成过滤器链的执行后在将其从 SecurityContextHolder 中清除。

  • HeadWriterFilter

    本过滤器用来向当前响应添加 headers,这对添加某些启用浏览器保护的 headers 可能会很有用。

    例如 X-Frame-Options、X-XSS-Protection 以及 X-Content-Type-Options。

    通过在 doFilterInternal() 方法中调用 HeaderWriterResponse 的 writeHeaders() 方法来向 response 中写入 headers。

    在 writeHeaders() 方法中会调用 OnCommittedResponseWrapper 的 isDisableOnResponseCommitted() 来判断是否需要向 response 中写入 headers。

  • CsrfFilter

    本过滤器使用了一个同步器令牌模式(synchronizer token pattern)来实现 CSRF保护。

    开发者应该确保对每个允许状态改变(allows state to change)的请求都调用了本过滤器,即应确保 Web 应用遵循了合适的 REST 语义(GET、HEAD、TRACE 和 OPTIONS 等方法不应有状态改变)。

    通常,CsrfTokenRepository 实现选择使用由 LazyCsrfTokenRepository 包装的 HttpSessionCsrfTokenRepository 在 HttpSession 中存储 CsrfToken。

    它优先(preferred to)将令牌存储在 cookie 中,该 cookie 可由客户端应用修改。

    在它的 doFilterInternal() 方法中使用了一个 RequestMatcher 来判断当前请求是否匹配 CSRF 的处理。

    默认的方式是忽略 GET、HEAD、TRACE 和 OPTIONS 请求,处理其他所有请求。

    若要禁用 CSRF,则需要在 WebSecurityConfigurerAdapter 的 configure(HttpSecurity http) 方法中调用 http.csrf().disable()。

  • LogoutFilter

    本过滤器用于处理来自 /logout 的请求,注销一个用户凭证(principal)。

    注销(logout)后会重定向到一个指定的 URL,这个 URL 由 LogoutSuccessHandler 或 logoutSuccessUrl(取决于使用的构造器)来决定。

    它会轮询一系列 LogoutHandler,处理器(handlers)应该按它们被需要的顺序指定。通常情况下需要调用 TokenBasedRememberMeServices 和 SecurityContextLogoutHandler。

    通过在 doFilter() 方法中调用 requiresLogout() 方法来判断当前请求是否需要注销。

    使用一个 RequestMatcher 来判断当前请求的 URL 是否匹配 /logout。

  • UsernamePasswordAuthenticationFilter

    本过滤器用于处理来自 /login 的请求,完成对用户请求认证的处理。它继承自 AbstractAuthenticationProcessingFilter 类。

    用户提交的身份验证表单(authentication form)需要提供 username 和 password 两个参数。

    参数名 username 和 password 可通过设置 usernameParameter 和 passwordParameter 属性来修改。

    在 AbstractAuthenticationProcessingFilter 的 doFilter() 方法中会调用 requiresAuthentication() 方法来判断当前请求是否需要认证。

    通过一个 RequestMatcher 来匹配当前请求的 URL 和 method。

  • ConcurrentSessionFilter

    本过滤器用于处理每个请求中的 session。

    若 session 未过期,在 doFilter() 方法中调用 SessionRegistry 的 refreshLastRequest() 方法来使 session 中数据总是最新。

    若 session 过期,在 doFilter() 方法中调用该过滤器的 doLogout() 方法,该方法会调用已配置的 logout handlers。

  • RequestCacheAwareFilter

    doFilter() 方法中调用 RequestCache 的 getMatchingRequest() 方法来查找当前请求是否已被缓存。

    若当前请求已被缓存,则使用重新构建(reconstituting)的请求替换当前请求。

  • SecurityContextHolderAwareReqeustFilter

    本过滤器使用一个实现了 servlet API 安全方法的请求包装器(request wrapper)包装 ServletRequest,使其具有更丰富的接口。

    这通过在 doFilter() 方法中调用 HttpServletRequestFactory 接口的 create() 方法来完成。

    用户请求在本过滤器中与 SecurityContext 关联。

  • RememberMeAuthenticationFilter

    在用户请求没有认证而直接访问资源时,本过滤器会查找请求中的 remember-me 信息并进一步处理。

    在 doFilter() 方法中,从 SecurityContext 中获取 Authentication,若 Authentication 不为 null,表示请求已认证,把请求传递给下一个过滤器,否则执行 remember-me 相关的处理。

    通过 RememberMeServices 的 autoLogin() 方法获取一个 Authentication,若 Authentication 为 null,表示没有 remember-me 信息,是一个匿名请求。然后把请求传递给下一个过滤器(AnonymousAuthenticationFilter)。

    若 Authentication 不为 null,则将其存入 SecurityContext,然后调用本过滤器的 onSuccessfulAuthentication() 方法进行后续处理。

  • AnonymousAuthenticationFilter

    本过滤器用于为匿名访问的用户建立匿名认证。

    doFilter() 方法会从 SecurityContext 中获取一个 Authentication,若该 Authentication 不为 null,表示已有一个匿名认证,然后把请求传递给下一个过滤器。

    若 Authentication 为 null 则调用 createAuthentication() 方法创建一个匿名认证并放入 SecurityContext 中。

  • SessionManagementFilter

    本过滤器负责 session 相关的行为,它会检查当前请求是否存在于 SecurityContextRepository 中并判断是否已认证。

    若已认证,则调用 SessionAuthenticationStrategy 的相关方法来执行任何 session 相关的活动,如激活会话固定保护机制(session-fixation protection mechanisms)或检查多个并发登录等。

  • ExceptionTranslationFilter

    本过滤器用于处理过滤器链中抛出的任何 AccessDeniedException 和 AuthenticationException 异常,它是 Java 异常和 HTTP 响应之间的桥梁。

  • SsoSecurityInterceptorImpl

    用户自定义的过滤器。

  • FilterSecurityInterceptor

    执行 HTTP 资源的安全性处理。

Spring Security 中的其他过滤器

除了前面分析到的过滤器外,Spring Security 还包含一些其他的过滤器,比如下面两个过滤器用于默认生成 login 和 logout 页面,这些过滤器等遇到的时候再具体分析。

  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter

Spring Security 中的过滤器的更多相关文章

  1. Spring Security 实战干货:图解Spring Security中的Servlet过滤器体系

    1. 前言 我在Spring Security 实战干货:内置 Filter 全解析对Spring Security的内置过滤器进行了罗列,但是Spring Security真正的过滤器体系才是我们了 ...

  2. spring security 11种过滤器介绍

    1.HttpSessionContextIntegrationFilter 位于过滤器顶端,第一个起作用的过滤器. 用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个Secu ...

  3. Spring Security(2):过滤器链(filter chain)的介绍

    上一节中,主要讲了Spring Security认证和授权的核心组件及核心方法.但是,什么时候调用这些方法呢?答案就是Filter和AOP.Spring Security在我们进行用户认证以及授予权限 ...

  4. Spring Security(四) —— 核心过滤器源码分析

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...

  5. 六:Spring Security 中使用 JWT

    Spring Security 中使用 JWT 1.无状态登录 1.1 什么是有状态? 1.2 什么是无状态 1.3 如何实现无状态 2.JWT 2.1 JWT数据格式 2.2 JWT交互流程 2.3 ...

  6. Spring Security:Servlet 过滤器(三)

    3)Servlet 过滤器 Spring Security 过滤器链是一个非常复杂且灵活的引擎.Spring Security 的 Servlet 支持基于 Servlet 过滤器,因此通常首先了解过 ...

  7. Spring Security配置个过滤器也这么卷

    以前胖哥带大家用Spring Security过滤器实现了验证码认证,今天我们来改良一下验证码认证的配置方式,更符合Spring Security的设计风格,也更加内卷. CaptchaAuthent ...

  8. [收藏]Spring Security中的ACL

    ACL即访问控制列表(Access Controller List),它是用来做细粒度权限控制所用的一种权限模型.对ACL最简单的描述就是两个业务员,每个人只能查看操作自己签的合同,而不能看到对方的合 ...

  9. Spring Security中html页面设置hasRole无效的问题

    Spring Security中html页面设置hasRole无效的问题 一.前言 学了几天的spring Security,偶然发现的hasRole和hasAnyAuthority的区别.当然,可能 ...

随机推荐

  1. Spark的优势

    首先了解一下Mapreduce 它最本质的两个过程就是Map和Reduce,Map的应用在于我们需要数据一对一的元素的映射转换,比如说进行截取,进行过滤,或者任何的转换操作,这些一对一的元素转换就称作 ...

  2. 转STM32官方固件库简介

    ST(意法半导体)为了方便用户开发程序,提供了一套丰富的 STM32 固件库.固件库就是函数的集合,固件库函数的作用是向下负责与寄存器直接打交道,向上提供用户函数调用的接口(API) .固件库将这些寄 ...

  3. ios -- 成员变量、实例变量与属性的区别

      最近打开手机就会被胡歌主演的<猎场>刷屏,这剧我也一直在追,剧中的郑秋冬,因为传销入狱五年,却在狱中拜得名师孙漂亮(孙红雷),苦学HR,并学习了心理学,成功收获两样法宝.出狱后因为怕受 ...

  4. 使用Swagger2时遇到的问题

    Swagger2使用起来很简单,加一个@EnableSwagger2注解,并引入如下依赖就ok了 <dependency> <groupId>io.springfox</ ...

  5. Spring源码分析之IoC容器初始化

    本文首发于cdream个人博客(点击获得更加阅读体验) 欢迎转载,转载请注明出处 作为一个java程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring ...

  6. vue2.X的路由

    以 / 开头的嵌套路径会被当作根路径. <router-link> 在vue-router1.X中是以<a v-link=""></a>存在的 ...

  7. Android根据图片Uri获取图片path绝对路径的几种方法【转】

    在Android 编程中经常会用到Uri转化为文件路径,如我们从相册选择图片上传至服务器,一般上传前需要对图片进行压缩,这时候就要用到图片的绝对路径. 下面对我开发中uri转path路径遇到的问题进行 ...

  8. 利用奇异值分解(SVD)进行图像压缩-python实现

    首先要声明,图片的算法有很多,如JPEG算法,SVD对图片的压缩可能并不是最佳选择,这里主要说明SVD可以降维 相对于PAC(主成分分析),SVD(奇异值分解)对数据的列和行都进行了降维,左奇异矩阵可 ...

  9. 性能测试—JMeter 常用元件(四)

    <零成本web性能测试>第三章 Web性能测试脚本录制与开发中JMeter常用测试元件 测试计划描述了JMeter运行时将会执行的一系列步骤,一个完整的测试计划包含一个或多个线程组.逻辑控 ...

  10. windows10 VM12 安装Mac OS X 10.11

    转载自:http://blog.csdn.net/j755ing/article/details/69400439 第一步: 下载 材料/工具: 下载 VMware Workstation 12 Pr ...