Spring Security 的 Filter 机制
# Spring Security 中的 Filter 机制
前言
Spring Security 的核心机制是基于 Servlet Filter 的,所有的安全控制逻辑(如认证、授权、会话管理、CSRF 防护等)都是通过一系列过滤器(Filter)来实现的。理解 Filter 链的工作原理,是学习 Spring Security 的基础。
# 1. 什么是 Servlet Filter?
Servlet Filter 是 Java Web 应用程序中用于拦截 HTTP 请求和响应的组件,可以在请求到达 Servlet 之前或响应返回给客户端之前,对请求或响应进行预处理或后处理。
在 Spring Security 中,Filter 机制被广泛用于实现各种安全功能,例如:
- 认证:验证用户的身份(如登录)。
- 授权:控制用户是否有权限访问特定资源。
- CSRF 防护:防止跨站请求伪造攻击。
# 2. Spring Security 中的 Filter 链(Filter Chain)
Spring Security 的核心是一个由多个过滤器组成的 Filter Chain,这个链条中的过滤器按特定顺序执行,确保每个请求都经过必要的安全检查。
典型的过滤器链流程:
- 请求进入:当用户请求访问应用时,Spring Security 的 Filter Chain 会首先处理请求。
- 过滤器依次执行:每个过滤器负责不同的安全逻辑(如认证、授权、会话管理等)。每个过滤器可以决定是否继续传递请求给下一个过滤器。
- 通过安全检查:如果所有过滤器都成功执行,最终请求会被传递给目标资源(如 Controller 中的方法)。
- 响应返回:当响应生成后,也会经过相同的过滤器链进行处理。
# 3. Spring Security 中的默认过滤器链及其顺序与作用
Spring Security 提供了一套默认的过滤器链,这些过滤器按特定顺序执行。以下是默认的过滤器顺序及其作用和使用场景:
WebAsyncManagerIntegrationFilter:
作用:集成 Spring 异步请求,使安全上下文在异步处理时仍然有效。
场景:在使用 Spring MVC 的异步请求时,确保安全上下文能够正确传播。SecurityContextPersistenceFilter:
作用:在请求开始时加载SecurityContext
(保存用户的认证信息),在请求结束时保存它,确保用户的认证状态在多个请求中保持一致。
场景:每次请求都能获取到当前用户的认证信息。HeaderWriterFilter:
作用:添加常见的安全 HTTP 头部(如X-Content-Type-Options
、X-Frame-Options
、X-XSS-Protection
),帮助防御常见的 Web 安全攻击。
场景:防御 XSS 和点击劫持等常见安全问题。CsrfFilter:
作用:防护跨站请求伪造(CSRF)攻击,通过生成和验证 CSRF Token 确保请求的合法性。
场景:在表单提交中启用,特别适用于 Web 应用的表单操作。LogoutFilter:
作用:处理用户的注销请求,清除SecurityContext
并使会话失效。
场景:用户点击“注销”按钮时,清除用户的认证信息。UsernamePasswordAuthenticationFilter:
作用:处理基于用户名和密码的登录认证,拦截登录请求并验证用户凭证。
场景:表单登录中常用,处理用户提交的用户名和密码。DefaultLoginPageGeneratingFilter:
作用:当没有自定义登录页面时,Spring Security 会自动生成一个默认的登录页面。
场景:项目初期或不需要自定义登录页面的场景。DefaultLogoutPageGeneratingFilter:
作用:当没有自定义注销页面时,Spring Security 会生成一个默认的注销页面。
场景:在没有自定义注销页面的情况下,提供基本的注销功能。BasicAuthenticationFilter:
作用:处理 HTTP Basic 认证,适用于 API 接口保护。
场景:用于保护 RESTful API 接口。RequestCacheAwareFilter:
作用:在用户认证成功后,重定向回用户之前尝试访问的受保护资源。
场景:用户在未登录时访问受保护页面,登录成功后重定向回原来的页面。SecurityContextHolderAwareRequestFilter:
作用:确保安全上下文中的信息可以在整个请求周期中被访问。
场景:保证HttpServletRequest
可以正确访问用户的认证信息和权限。AnonymousAuthenticationFilter:
作用:为未认证的用户提供匿名身份,避免系统抛出未认证的异常。
场景:需要对未登录用户提供基本访问权限时(如公开页面)。SessionManagementFilter:
作用:管理用户的会话,包括并发会话控制、会话固定攻击防护等。
场景:严格管理用户会话的场景,如限制同一用户多地登录。ExceptionTranslationFilter:
作用:处理认证或授权过程中产生的异常,如AccessDeniedException
或AuthenticationException
。
场景:用户访问未授权资源或未登录时使用。FilterSecurityInterceptor:
作用:Spring Security 的核心过滤器,负责最终的权限检查。
场景:结合配置或注解(如@PreAuthorize
)进行资源的访问控制。
# 4. Filter 链的执行顺序的重要性
过滤器的顺序非常重要,Spring Security 已根据常见的安全场景预定义了这些过滤器的执行顺序。例如:
- SecurityContextPersistenceFilter 应在其他过滤器之前执行,以确保加载用户的认证信息。
- ExceptionTranslationFilter 需要在权限检查前执行,以便捕获并处理访问受限的异常。
- FilterSecurityInterceptor 是最终执行的过滤器,因为它决定了用户是否有权限访问目标资源。
# 5. 自定义和扩展过滤器链
虽然 Spring Security 提供了默认的过滤器链,但你可以根据需求自定义和扩展它。例如,添加自定义的 JWT 认证过滤器或日志记录过滤器。
代码示例:添加自定义过滤器
import javax.servlet.FilterChain;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class CustomFilter extends UsernamePasswordAuthenticationFilter {
@Override
public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 自定义过滤逻辑,例如检查额外的安全参数
if (request.getMethod().equalsIgnoreCase("POST") && request.getRequestURI().equals("/login")) {
System.out.println("自定义过滤器:处理登录请求");
}
// 继续执行下一个过滤器或目标资源
chain.doFilter(request, response);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
将自定义 Filter 添加到 Filter 链中:
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class) // 在指定过滤器之前插入自定义过滤器
.authorizeRequests()
.anyRequest().authenticated() // 保护所有请求
.and()
.formLogin(); // 启用表单登录
return http.build();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
解释:
addFilterBefore()
:将自定义过滤器插入到指定的过滤器之前。这里我们将CustomFilter
放在UsernamePasswordAuthenticationFilter
之前。
# 6. 自动配置的过滤器机制
这些默认过滤器是由 Spring Security 自动配置和管理的。在大多数情况下,你不需要直接操作这些过滤器。它们会根据你的配置自动进行安全处理。
总结
- 自动配置:Spring Security 会根据你的项目需求(如启用表单登录、HTTP Basic 认证等)自动加载和管理这些过滤器。
- 无需手动干预:这些过滤器已经满足常见的安全需求。你只需通过配置类或注解进行设置。
- 自定义扩展:在特殊需求下,可以添加自定义过滤器来扩展默认行为。