Spring Security 安全过滤器
1. 前言
上一节中我们了解到了 Spring Security 安全框架是由一系列有序的安全过滤器组成的,本节将重点讨论 Spring Security 内置安全过滤器的顺序及含义。
Spring 将自己体系内的过滤器交由「过滤器代理 FilterChainProxy」管理,Spring Security 在「过滤器代理 FilterChainProxy」中加入了「安全过滤器链 SecurityFilterChain」实现安全保护功能。
在 Spring Security 各个模块中,内置已实现了一系列的「安全过滤器」,可以满足常见的认证、鉴权等功能需求。
在 Spring Security 5.3.2 中,共内置了 33 个安全过滤器。
本节主要讨论 Spring Security 内置的安全过滤器。
2. 内置安全过滤器
2.1 内置安全过滤器的声明
Spring Security 中的 SecurityFilterChain
对象,是通过 HttpSecurity
类来使用,它还提供了安全过滤器增、减的调用接口。
如前文所述,安全过滤器的执行顺序是至关重要的,而顺序的确定是由 FilterComparator
对象实现的。
FilterComparator
类的全路径为 org.springframework.security.config.annotation.web.builders.FilterComparator
。
在其构造的时候配置了过滤器的基本顺序,代码如下:
FilterComparator() {
FilterComparator.Step order = new FilterComparator.Step(100, 100);
this.put(ChannelProcessingFilter.class, order.next());
this.put(ConcurrentSessionFilter.class, order.next());
this.put(WebAsyncManagerIntegrationFilter.class, order.next());
this.put(SecurityContextPersistenceFilter.class, order.next());
this.put(HeaderWriterFilter.class, order.next());
...
this.put(SessionManagementFilter.class, order.next());
this.put(ExceptionTranslationFilter.class, order.next());
this.put(FilterSecurityInterceptor.class, order.next());
this.put(SwitchUserFilter.class, order.next());
}
注意,以上
FilterComparator
中的定义仅作为内置过滤器排序的依据,并不等于过滤器的真正注入。
除了内置的过滤器外,当我们需要添加自定义过滤器的时候,也需要依赖 FilterComparator
对象完成顺序的配置。实际开发时可用以下方法完成向指定顺位插入过滤器的功能:http.addFilterAfter
、http.addFilterBefore
、http.addFilter
、http.addFilterAt
(http
为 HttpSecurity
实例)。
另一方面,HttpSecurity
还为各个内置过滤器提供了配置接口。例如:针对 HeaderWriterFilter
提供了 headers()
方法,用于获取或为 HeaderWriterFilter
配置参数,例如设置响应缓存时效 Expires
等 。HttpSecurity
在构建时,会根据已有的配置信息逐一对 Filter
进行实例化。HttpSecurity
还完成了对请求地址的配置,通过 RequestMatcherConfigurer
对象使请求地址与 Filter
匹配。
2.2 内置过滤器总览
顺序号 | 过滤器名称 | 简述 |
---|---|---|
1 | ChannelProcessingFilter | 检查 web 请求通道,如:http、https |
2 | ConcurrentSessionFilter | 检查 Session 状态,更新 Session 最后访问时间 |
3 | WebAsyncManagerIntegrationFilter | 关联 Spring Web 上下文和 Spring Security 上下文 |
4 | SecurityContextPersistenceFilter | 从 Session 构建 SecurityContext |
5 | HeaderWriterFilter | 往请求头或响应头里写入信息 |
6 | CorsFilter | 跨域请求头 |
7 | CsrfFilter | 跨站请求伪造 |
8 | LogoutFilter | 注销过滤器 |
9 | OAuth2AuthorizationRequestRedirectFilter | OAuth2 请求重定向 |
10 | Saml2WebSsoAuthenticationRequestFilter | SAML2 单点登录认证请求过滤器 |
11 | X509AuthenticationFilter | X509 认证过滤器 |
12 | AbstractPreAuthenticatedProcessingFilter | 预认证处理 |
13 | CasAuthenticationFilter | 单点认证过滤器 |
14 | OAuth2LoginAuthenticationFilter | OAuth2 认证过滤器 |
15 | Saml2WebSsoAuthenticationFilter | SAML2 单点登录认证过滤器 |
16 | UsernamePasswordAuthenticationFilter | 用户名密码认证过滤器 |
17 | ConcurrentSessionFilter | 检查 Session 状态,更新 Session 最后访问时间。第二次出现。 |
18 | OpenIDAuthenticationFilter | Open ID 认证过滤器 |
19 | DefaultLoginPageGeneratingFilter | 生成 /login 页面 |
20 | DefaultLogoutPageGeneratingFilter | 生成 /logout 页面 |
21 | DigestAuthenticationFilter | 数字签名认证过滤器 |
22 | BearerTokenAuthenticationFilter | Bearer Token 认证过滤器 |
23 | BasicAuthenticationFilter | 基本身份认证过滤器 |
24 | RequestCacheAwareFilter | 缓存请求状态过滤器 |
25 | SecurityContextHolderAwareRequestFilter | 安全上下文请求辅助过滤器 |
26 | JaasApiIntegrationFilter | JAAS 认证授权过滤器 |
27 | RememberMeAuthenticationFilter | 实现记住我功能 |
28 | AnonymousAuthenticationFilter | 匿名认证过滤器 |
29 | OAuth2AuthorizationCodeGrantFilter | OAuth2 认证授权码 |
30 | SessionManagementFilter | 管理 Session |
31 | ExceptionTranslationFilter | 异常事件处理过滤器 |
32 | FilterSecurityInterceptor | 动态权限配置 |
33 | SwitchUserFilter | 切换账户 |
2.3 常用内置过滤器的具体说明
内置过滤器的参数设置通过 HttpSecurity 相应的配置方法完成。
2.3.1 ChannelProcessingFilter
ChannelProcessingFilter
的用于检测请求的通道,例如 Http
或 Https
等,可以实现访问请求在不同通道间的跳转。
ChannelProcessingFilter
的配置通过 HttpSecurity.requiresChannel()
方法获取。
例如:强制使用 Https 通道访问。
http.requiresChannel().antMatchers("/users").requiresSecure();
2.3.2 ConcurrentSessionFilter
此过滤器在默认情况下出现两次,其工作内容大致分两步:
- 判断 Session 是否存在,如果存在则获取,否则结束;
- 判断 Session 是否过期,如果过期则执行退出操作,否则更新 Session 时间。
ConcurrentSessionFilter
的配置通过 HttpSecurity.sessionManagement()
方法获取。
例如:设置 Session 无效时的跳转 URL。
http.sessionManagement().invalidSessionUrl("/login");
2.3.3 WebAsyncManagerIntegrationFilter
WebAsyncManagerIntegrationFilter
用于关联 SecurityContext 上下文。
此过滤器无配置公布的方法。
2.3.4 SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
用于从 Session 构建 SecurityContext。具体分为两步:
- 请求开始时,将 SecurityContextRepository 中的 SecurityContext 对象存入 SecurityContextHolder;
- 请求完成时,清理 SecurityContextHolder 中的 SecurityContext 对象,并产生新的 SecurityContext 对象放入到 SecurityContextRepository 中,以保证并发环境下的数据一致性。
SecurityContextPersistenceFilter
的配置通过 HttpSecurity.securityContext()
方法获取。
2.3.5 HeaderWriterFilter
HeaderWriterFilter
用于往请求头或响应头里写入信息。
HeaderWriterFilter
的配置通过 HttpSecurity.headers()
方法获取。默认支持的 Header 包括:
Header [name: X-Content-Type-Options, values: [nosniff]]
Header [name: X-XSS-Protection, values: [1; mode=block]]
Header [name: Cache-Control, values: [no-cache, no-store, max-age=0, must-revalidate]]
Header [name: Pragma, values: [no-cache]]
Header [name: Expires, values: [0]]
2.3.6 CorsFilter
CorsFilter
用于配置跨域请求策略。当一个请求中,来源与目标的协议、主机名、端口三者任一不同,即为跨域,在实际开发中如果遇到类似 header is present on the requested resource.
的错误时,往往是因为跨域配置不正确导致。
CorsFilter
的配置通过 HttpSecurity.cors()
方法获取。例如,禁用跨域验证:
http.cors().disable();
2.3.7 CsrfFilter
CsrfFilter
用于验证消息来源,防范跨站请求伪造,此项功能需要前端的配合。
CsrfFilter
的配置通过 HttpSecurity.csrf()
方法获取。例如,禁用 Csrf:
http.csrf().disable();
2.3.8 LogoutFilter
LogoutFilter
用于注销登录状态。
LogoutFilter
的配置通过 HttpSecurity.logout()
方法获取。例如,设置退出后的跳转页面。
http.logout().logoutSuccessUrl("/login");
2.3.9 UsernamePasswordAuthenticationFilter
UsernamePasswordAuthenticationFilter
用于处理用户名、密码认证。
UsernamePasswordAuthenticationFilter
的配置通过 HttpSecurity.formLogin()
方法获取。例如,设置用户名参数为「mobile」:
http.formLogin().usernameParameter("mobile")
UsernamePasswordAuthenticationFilter
Spring Security 认证中较为常用的过滤器,我们会在后续章节重点展开。
2.3.10 ExceptionTranslationFilter
ExceptionTranslationFilter
用于异常事件处理。异常事件有前述过滤器抛出,异常共分为 2 类,一类是认证异常,另一类是权限异常。
ExceptionTranslationFilter
的配置通过 HttpSecurity.exceptionHandling()
方法获取。
ExceptionTranslationFilter
同样较为常用,将在后续章节中重点展开。
3 增加自定义的 Filter
在 HttpSecurity 对象中增加自定义 Filter 可用于实现认证方式的扩展等场景,扩展 Filter 有两步很重要,第一是实现 javax.servlet.Filter 接口;第二是指定新过滤器的位置。
例如,扩展自定义接口 SimpleFilter。
- 自定义接口类
public class SimpleFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("In SimpleFilter");
}
}
- 加入到指定位置,比如加在
UsernamePasswordAuthenticationFilter
之前
http.addFilterBefore(new SimpleFilter(), UsernamePasswordAuthenticationFilter.class);
4. 小结
本节我们介绍了 Spring Security 过滤器的作用及其顺序,主要的知识点有:
- Spring Security 通过扩展 Spring 过滤器实现安全相关业务逻辑;
- Spring Security 目前原生实现了 33 个过滤器,每个过滤器有固定的顺序及应用场景;
- Spring Security 可以的插入自定义的过滤器,并且指定过滤器的位置。
Spring Security 过滤器几乎是所有安全功能的核心,在后续章节中还会围绕过滤器展开讨论。下节我们将介绍 Spring Security 的异常回收机制。