深入理解 Spring Security 的过滤器体系,是掌握整个安全框架的基石。本文从源码层面剖析 30+ 过滤器的注册机制、三大类过滤器的职责边界,以及在前后端分离 + JWT 场景下的最佳配置策略。基于 Spring Security 6.x 源码。
前言:为什么过滤器链是你的"源码地图"?
Spring Security 的Web 安全层 建立在 Servlet Filter 之上------它不通过传统的"拦截器"来实现 URL 级别的安全控制,而是忠实地遵循 Java Web 规范,将每一个安全功能封装为一个标准的 jakarta.servlet.Filter。 但是,Spring Security 还提供了方法级别的安全控制,这部分是基于 Spring AOP 实现的 (如 @PreAuthorize、@PostAuthorize 等注解)。
一个 HTTP 请求进入应用后,会像流水线一样经过一条精心编排的过滤器链。 流水线上的每个"工位"(过滤器)各司其职:
| 工位角色 | 对应过滤器 | 做什么 |
|---|---|---|
| 身份登记处 | SecurityContextHolderFilter |
从 Session/Token 中恢复"你是谁" |
| 安检门 | CsrfFilter |
检查请求是否来自正规渠道 |
| 前台签到 | UsernamePasswordAuthenticationFilter |
处理用户名密码登录 |
| 门禁系统 | AuthorizationFilter |
检查"你能否进这个房间" |
| 异常调度员 | ExceptionTranslationFilter |
踢人(未登录)或拦截(无权限) |
如果你连过滤器的执行顺序都搞不清楚,调试源码时会彻底迷失。 比如你自定义了一个 JWT 过滤器,却发现它"不生效"------很可能只是放错了位置!
本文将围绕三个核心问题展开:
- "宪法"问题:Spring Security 如何定义 30+ 过滤器的严格执行顺序?
- "开关"问题:哪些过滤器是"天生就在的"(核心骨架),哪些是"Spring Boot 帮你开的"(自动配置),哪些必须"你自己动手"(手动开启)?
- "取舍"问题:在前后端分离 + JWT 的场景下,哪些过滤器成"累赘"需要关掉?
一、FilterOrderRegistration:过滤器顺序的"宪法"
1.1 什么是 FilterOrderRegistration?
FilterOrderRegistration 是 Spring Security 内部的一个注册表类,它定义了所有安全过滤器的默认执行顺序。可以把它理解为过滤器链的"宪法"------每个过滤器在该注册表中都被分配了一个整数值,数值越小,优先级越高,越先执行。
1.2 完整源码解析
FilterOrderRegistration 的核心逻辑在其构造方法中------一个 Step 对象,每次调用 next() 增加 ORDER_STEP(默认 100)。注意:FilterOrderRegistration 只是定义了顺序,并不代表所有过滤器都开启了------它的角色是"宪法",而不是"执行名单"。
以下是框架内预定义的所有过滤器及其顺序:
java
final class FilterOrderRegistration {
private final Map<String, Integer> filterToOrder = new HashMap<>();
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
// 阶段一:请求预处理
put(DisableEncodeUrlFilter.class, order.next()); // 禁用 Servlet 响应的 URL 编码
put(ForceEagerSessionCreationFilter.class, order.next()); // 强制 Session 创建
put(ChannelProcessingFilter.class, order.next()); // 通道安全(HTTP→HTTPS)
put(HttpsRedirectFilter.class, order.next()); // HTTPS 重定向
order.next(); // gh-8105 预留位
// 阶段二:安全上下文与基础安全
put(WebAsyncManagerIntegrationFilter.class, order.next()); // 将 SecurityContext 传播到 Spring MVC 的异步请求处理中
put(SecurityContextHolderFilter.class, order.next()); // SecurityContext 持有者(5.7+ 新增, 替代下面的持久化过滤器)
put(SecurityContextPersistenceFilter.class, order.next()); // @Deprecated(5.7), 6.x 默认不再使用
put(HeaderWriterFilter.class, order.next()); // 安全响应头
put(CorsFilter.class, order.next()); // 跨域处理
put(CsrfFilter.class, order.next()); // CSRF 防护
put(LogoutFilter.class, order.next()); // 登出处理
// 阶段三:OAuth2/SAML2 请求重定向
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
this.filterToOrder.put(
"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(GenerateOneTimeTokenFilter.class, order.next()); // 一次性 Token
// 阶段四:各类认证过滤器(按优先级排列)
put(X509AuthenticationFilter.class, order.next()); // X.509 证书认证
put(AbstractPreAuthenticatedProcessingFilter.class, order.next()); // 预认证(SSO)
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter", order.next());
this.filterToOrder.put("org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter", order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next()); // ★ 表单登录(最常用)
put(OneTimeTokenAuthenticationFilter.class, order.next()); // 一次性 Token 认证
order.next(); // gh-8105 预留位
// 阶段五:默认页面与并发会话
put(DefaultResourcesFilter.class, order.next()); // 服务于框架自动生成页面所需的默认资源
put(DefaultLoginPageGeneratingFilter.class, order.next()); // 默认登录页生成
put(DefaultLogoutPageGeneratingFilter.class, order.next()); // 默认登出页生成
put(DefaultOneTimeTokenSubmitPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next()); // 并发会话控制
put(DigestAuthenticationFilter.class, order.next()); // Digest 认证
// 阶段六:Bearer Token 认证(RESTful API)
this.filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",
order.next());
put(BasicAuthenticationFilter.class, order.next()); // HTTP Basic 认证
put(AuthenticationFilter.class, order.next()); // 通用认证过滤器
put(RequestCacheAwareFilter.class, order.next()); // 请求缓存恢复
put(SecurityContextHolderAwareRequestFilter.class, order.next()); // Servlet API 适配
put(JaasApiIntegrationFilter.class, order.next()); // JAAS 集成
put(RememberMeAuthenticationFilter.class, order.next()); // 记住我
put(AnonymousAuthenticationFilter.class, order.next()); // ★ 匿名认证
// 阶段七:OAuth2
this.filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next()); // 会话管理
put(ExceptionTranslationFilter.class, order.next()); // ★ 异常翻译(核心)
put(FilterSecurityInterceptor.class, order.next()); // 授权拦截器(旧版)
put(AuthorizationFilter.class, order.next()); // ★ 授权过滤器(新版)
put(SwitchUserFilter.class, order.next()); // 用户切换(模拟登录)
}
}
关键设计细节解读:
| 设计点 | 源码体现 | 含义 |
|---|---|---|
| 步长机制 | Step(INITIAL_ORDER, ORDER_STEP),ORDER_STEP=100,INITIAL_ORDER = 100 |
两个相邻过滤器之间预留了 99 个"座位",你自定义的过滤器可以轻松插入任意两个框架过滤器之间 |
| 字符串 Key 的特殊存在 | this.filterToOrder.put("o.s.s.oauth2...Filter", order.next()) |
OAuth2、SAML2、CAS 等过滤器使用完整类名字符串 而非 .class 注册,因为它们可能不在 classpath 中(避免 ClassNotFoundException) |
| gh-8105 预留位 | 两处 order.next() 不放入任何过滤器 |
这是 GitHub Issue 8105 的产物------为未来扩展或社区自定义过滤器预留的"空座位" |
| HashMap 存储 | filterToOrder 是 HashMap<String, Integer> |
Key 是过滤器类名或全限定名,Value 是整数序号。序号越小,越先执行 |
1.3 从"顺序注册表"到"实际执行顺序"的完整对照表
"顺序注册表"是全局的、静态的排序参考系;"实际执行顺序"是局部的、动态的、经过筛选后按该参考系排列的最终结果。
| 序号 | 过滤器 | 类别 | 职责一句话 |
|---|---|---|---|
| 1 | DisableEncodeUrlFilter |
预处理 | 禁用 Servlet 响应的 URL 编码,主要目的是防止 Session ID 被嵌入 URL 导致泄露 |
| 2 | ForceEagerSessionCreationFilter |
预处理 | 强制提前创建 Session |
| 3 | ChannelProcessingFilter |
通道安全 | HTTP → HTTPS 强制跳转 |
| 4 | HttpsRedirectFilter |
通道安全 | HTTPS 重定向 |
| 5 | WebAsyncManagerIntegrationFilter |
上下文 | 将 SecurityContext 传播到 Spring MVC 的异步请求处理中 |
| 6 | SecurityContextHolderFilter |
上下文 | 6.x 核心:请求开始加载 SecurityContext |
| 7 | SecurityContextPersistenceFilter |
上下文 | 5.x 兼容:请求开始加载 + 结束保存 SecurityContext |
| 8 | HeaderWriterFilter |
基础安全 | 写入 X-Frame-Options、HSTS 等安全响应头 |
| 9 | CorsFilter |
基础安全 | 处理跨域资源共享(需手动 http.cors() 开启) |
| 10 | CsrfFilter |
基础安全 | CSRF Token 校验(默认开启) |
| 11 | LogoutFilter |
基础安全 | /logout 请求处理:销毁 Session |
| 12 | OAuth2AuthorizationRequestRedirectFilter |
OAuth2 | OAuth2 登录入口重定向 |
| 13 | X509AuthenticationFilter |
认证 | X.509 证书认证 |
| 14 | UsernamePasswordAuthenticationFilter |
认证★ | 表单登录:POST /login → 账号密码认证 |
| 15 | DefaultLoginPageGeneratingFilter |
页面生成 | 如果未自定义登录页,生成默认 HTML 登录表单 |
| 16 | DefaultLogoutPageGeneratingFilter |
页面生成 | 如果未自定义登出页,生成默认登出确认页 |
| 17 | ConcurrentSessionFilter |
会话 | 并发会话控制(同一账号多端登录限制) |
| 18 | BearerTokenAuthenticationFilter |
认证 | OAuth2 Resource Server:Bearer Token 认证 |
| 19 | BasicAuthenticationFilter |
认证 | HTTP Basic Auth:请求头 Authorization: Basic xxx |
| 20 | RequestCacheAwareFilter |
缓存 | 检查是否有被缓存的原始请求(如登录前的 URL),如有则重定向到缓存的 URL |
| 21 | SecurityContextHolderAwareRequestFilter |
适配 | 包装 request,提供 Servlet 3.0 安全 API |
| 22 | RememberMeAuthenticationFilter |
认证 | "记住我"Cookie 自动登录 |
| 23 | AnonymousAuthenticationFilter |
认证 | 给未登录用户一个"匿名身份" |
| 24 | SessionManagementFilter |
会话 | 会话固定攻击防护 + 并发控制 |
| 25 | ExceptionTranslationFilter |
核心★ | 捕获 AuthenticationException / AccessDeniedException |
| 26 | AuthorizationFilter |
授权★ | 6.x 默认授权:检查用户是否有权访问该 URL |
| 27 | SwitchUserFilter |
调试 | 管理员模拟其他用户登录(ROLE_PREVIOUS_ADMINISTRATOR) |
重要认知 :从源码的
FilterOrderRegistration能看出来,认证相关的过滤器统一排在授权之前 (第 6-24 位),异常处理(第 25 位)紧挨着授权过滤器(第 26 位)。这个顺序是精心设计的:先搞清楚你是谁(认证),才能判断你能做什么(授权),异常处理器紧跟其后,一旦授权拒绝立刻捕获并转为 HTTP 响应。
二、核心过滤器深度拆解
Spring Security 的过滤器可按照功能职责以分为四大类,理解这个分类是后续所有实战决策的基础:
2.1 第一类:基础设施层
构成安全框架的底层骨架,通常由 HttpSecurity 默认构建,极难也不建议禁用。它们不直接处理业务认证,而是为整个链条提供运行环境。
① SecurityContextHolderFilter (6.x 核心)
核心源码行为:
scss
请求进入时 → 从 SecurityContextRepository 加载 SecurityContext → 设置到 SecurityContextHolder
请求结束时 → SecurityContextHolder.clearContext() 清理线程变量
6.x 关键变化:
SecurityContextPersistenceFilter(旧版):负责加载 和保存 SecurityContext,请求结束时会调用repository.saveContext()SecurityContextHolderFilter(6.x 新增):仅负责从SecurityContextRepository加载SecurityContext到当前线程,并在请求结束时清理线程变量。更轻量,更符合"单一职责"
实战影响 :如果你使用 JWT 无状态方案,可以显式设置 SecurityContextRepository 为 NullSecurityContextRepository,避免任何不必要的 Session 操作:
java
http.securityContext(context -> context
.securityContextRepository(new NullSecurityContextRepository()));
② ExceptionTranslationFilter
这是整个过滤器链中"最智能"的过滤器,但很多人不理解它的价值。
它位于 AuthorizationFilter 之前(注意:这是关键!),意味着它能捕获后续过滤器抛出的异常:
java
// 伪代码:ExceptionTranslationFilter 的核心逻辑
try {
chain.doFilter(request, response); // 执行后续过滤器
} catch (AuthenticationException e) {
// 未认证 → 重定向到登录页 或 返回 401
this.authenticationEntryPoint.commence(request, response, e);
} catch (AccessDeniedException e) {
// 已认证但无权限 → 返回 403 Forbidden
this.accessDeniedHandler.handle(request, response, e);
}
两种异常的区分逻辑:
AuthenticationException:用户未登录 → 保存当前请求到 Cache,重定向到/loginAccessDeniedException:用户已登录但权限不足 → 返回 403 Forbidden(不会重定向到登录页,因为重定向了也没用------用户已经登录了,只是没权限)
③ AuthorizationFilter(6.x 默认)
位于 ExceptionTranslationFilter 之后 、SwitchUserFilter 之前。它是业务请求到达 Controller 前的最后一道安全关卡(但并非物理链尾)。
核心逻辑极为简洁:
java
AuthorizationResult result = authorizationManager.authorize(authentication, request);
if (result != null && !result.isGranted()) {
throw new AccessDeniedException("Access Denied"); // 鉴权失败时抛出 AccessDeniedException,交由上游的 ExceptionTranslationFilter 统一处理。
}
为什么放在过滤器链末尾? 因为授权校验必须在所有认证过滤器执行完之后------只有先确定了用户身份(经过了前面的各种认证过滤器),才能判断他能不能访问这个 URL。
2.2 第二类:认证支持层:身份识别的执行者
负责从请求中提取凭证并验证身份。可通过 .formLogin().disable()、.httpBasic().disable() 等方式按需关闭。
① UsernamePasswordAuthenticationFilter
| 维度 | 说明 |
|---|---|
| 触发原因 | 拦截 POST /login 表单提交。 |
| 请求参数 | username + password(表单参数) |
| 核心动作 | 提取 username/password → 封装 UsernamePasswordAuthenticationToken → 委托 AuthenticationManager 验证 → 成功后将 Authentication 存入 SecurityContext。 |
| 关闭方式 | http.formLogin(form -> form.disable()) |
| Spring Boot 默认行为 | 引入 spring-boot-starter-security 后自动开启。 |
下一篇会完整拆解这个过滤器从
attemptAuthentication到SecurityContext保存的全链路源码。
② BasicAuthenticationFilter
| 维度 | 说明 |
|---|---|
| 触发原因 | HttpSecurity 默认启用(非 Boot 特有);Boot 项目中未显式 disable 时自动生效 |
| 触发条件 | 请求头包含 Authorization: Basic <Base64(username:password)> |
| 核心动作 | 提取 Header → Base64 解码 → 封装 Token → 调用 AuthenticationManager.authenticate() |
| 典型场景 | Postman 测试、微服务间调用、curl 命令行 |
| 关闭方式 | http.httpBasic(basic -> basic.disable()) |
③ AnonymousAuthenticationFilter
很多人不理解为什么要给"没登录的人"一个身份。 这其实是一个防止空指针的优雅设计。
java
// AnonymousAuthenticationFilter 的核心逻辑
if (SecurityContextHolder.getContext().getAuthentication() == null) {
// 创建一个匿名身份:"anonymousUser",角色为 ROLE_ANONYMOUS
SecurityContextHolder.getContext().setAuthentication(
new AnonymousAuthenticationToken("anonymousUser", "anonymousUser",
List.of(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))));
}
为什么需要匿名身份?
- 如果没有匿名身份,未登录用户的
Authentication对象就是null - 后续的
AuthorizationFilter中,permitAll()需要判断:这个"没有身份的人"是否被允许访问? - 有了
AnonymousAuthenticationToken,系统可以统一处理:ROLE_ANONYMOUS是一个明确标识,权限检查逻辑不需要处理null特例
④ RequestCacheAwareFilter
配合表单登录使用。当未登录用户访问了一个需要认证的 URL 时:
ExceptionTranslationFilter将当前请求保存到RequestCache- 用户被重定向到登录页
- 登录成功后,
RequestCacheAwareFilter从 Cache 中取出原始请求并重放 - 用户直接看到登录前想看的页面(而不是被扔到首页)
2.3 第三类:安全增强层:防御策略的实施者
提供 CSRF、CORS、安全头等横切面防护。大多数可按需禁用,但禁用前必须评估安全风险。
① CsrfFilter:前后端分离最常被误解的过滤器
这是整个 Spring Security 中最容易被误解、也最常被误关或误开的过滤器。让我们仔细讲清楚。
CSRF 攻击的本质:
- 用户登录了
bank.com,浏览器存了 Session Cookie - 用户访问了恶意网站
evil.com - 恶意网站向
bank.com/transfer?to=hacker&amount=10000发送请求 - 浏览器自动带上了
bank.com的 Cookie! → 请求被服务器认为是合法的 → 钱被转走
CsrfFilter 的防御原理:
- 服务器生成一个随机 Token,存入 Session
- 将这个 Token 以隐藏字段的形式嵌入表单
- 表单提交时,服务器校验表单中的 Token 与 Session 中的 Token 是否一致
- 恶意网站无法读取
bank.com页面中的 Token(同源策略),所以攻击失败
为什么 JWT 场景不需要 CSRF 防护?
这是最关键的认知:
| 维度 | Session 模式 | JWT 模式 |
|---|---|---|
| 认证凭证位置 | Cookie(JSESSIONID) |
客户端 localStorage 或内存 |
| 凭证发送方式 | 浏览器自动携带 | 前端手动设置 Authorization Header |
| CSRF 攻击可行性 | ✅ 可行:跨站请求会自动带 Cookie | ❌ 不可行:跨站请求不会自动带 Authorization Header |
| 是否需要 CsrfFilter | ✅ 需要 | ❌ 不需要 |
JWT 免疫 CSRF 的底层原因:
- 浏览器不会 在跨站请求中自动添加
Authorization头 - 跨站脚本无法读取 目标域的
localStorage(受同源策略保护) - 因此,攻击者无法伪造携带有效 JWT 的请求 → CSRF 攻击失败
java
// JWT 项目标配:关闭 CSRF
http.csrf(csrf -> csrf.disable());
⚠️ 警告:如果你把 JWT 存到了 Cookie 中(而不是 localStorage),那么 CSRF 风险依然存在!JWT 的 CSRF 免疫建立在"客户端主动设置请求头"这个前提之上。
② CorsFilter:跨域资源共享
CorsFilter 在 FilterOrderRegistration 中有注册顺序,但默认不会自动加入过滤器链,需要手动开启:
java
http.cors(Customizer.withDefaults());
同时必须提供 CorsConfigurationSource Bean:
java
// 方式一:使用全局 Bean(推荐)
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://your-frontend.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
config.setMaxAge(3600L); // 预检请求缓存时间(秒)
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
// 方式二:在 SecurityFilterChain 中直接配置
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://your-frontend.com"));
config.setAllowedMethods(List.of("GET", "POST"));
config.setAllowedHeaders(List.of("*"));
return config;
}))
// ... 其他配置
;
return http.build();
}
注意 :CorsFilter 在过滤器链中排在 CsrfFilter 之前 。这意味着 CORS 的预检请求(OPTIONS)会先被处理,不会被 CSRF 拦截。因为浏览器发送的 OPTIONS 预检请求不会携带自定义 Header 或 Cookie,必然无法通过 CSRF 校验------若 CorsFilter 不在其前面,所有跨域请求都会失败。
③ HeaderWriterFilter
向 HTTP 响应中写入安全 Header。默认写入以下头信息:
| HTTP Header | 值 | 防护目标 |
|---|---|---|
X-Content-Type-Options |
nosniff |
阻止浏览器 MIME 类型嗅探 |
X-Frame-Options |
DENY |
防止页面被嵌入 iframe(点击劫持) |
X-XSS-Protection |
0 |
禁用浏览器过时的 XSS 过滤器(避免副作用) |
Strict-Transport-Security |
max-age=31536000 |
强制 HTTPS(仅 HTTPS 请求) |
Cache-Control |
no-cache, no-store, max-age=0, must-revalidate |
防止安全页面被缓存 |
可通过
http.headers(h -> h.disable())完全关闭,或精细控制每个 Header 的开关。不属于不可移除的核心骨架。
2.4 第四类:扩展/自定义层:业务安全的延伸
完全由开发者控制,包括 OAuth2/SAML2/CAS 等协议过滤器及自定义 JWT Filter。
- 自定义 JWT Filter 插入位置 :推荐放在
UsernamePasswordAuthenticationFilter之前 (或同一位置),且必须在ExceptionTranslationFilter之前 。- JWT 解析本质是认证行为,需在授权过滤器之前完成身份填充;放在 ExceptionTranslationFilter 之前是为了确保 Token 解析异常能被正确捕获并返回 401;放在 UsernamePasswordAuthenticationFilter 之前是为了让 JWT 认证优先于表单认证,避免冲突。
- 可选依赖过滤器 :OAuth2、SAML2、CAS 等过滤器在
FilterOrderRegistration中使用全限定类名字符串 注册(而非.class),以避免在未引入对应依赖时抛出ClassNotFoundException。
三、「最小过滤器链」实战:JWT 前后端分离场景
综合以上分析,一个典型的前后端分离 + JWT 场景的标准配置如下。注意每一行配置背后对应着哪些过滤器的开启/关闭:
java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ① 关闭 CSRF------JWT 不存在 CSRF 风险(跨站请求不会自动带 Authorization Header)
.csrf(csrf -> csrf.disable())
// ② 开启 CORS------前后端分离,前后端通常不同域
.cors(Customizer.withDefaults())
// ③ 无状态 Session------JWT 不需要服务端 Session 存储
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// ④ 关闭表单登录------不用默认登录页,登录接口自己写 Controller
.formLogin(form -> form.disable())
// ⑤ 关闭 HTTP Basic------不需要
.httpBasic(basic -> basic.disable())
// ⑥ 关闭默认登出页------前后端分离不需要默认 HTML 确认页面
.logout(logout -> logout.disable())
// ⑦ 不需要请求缓存------登录成功后的跳转由前端控制
.requestCache(cache -> cache.disable())
// ⑧ 添加自定义 JWT 过滤器
.addFilterBefore(jwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
// ⑨ 授权规则
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**", "/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
3.1 精简后的过滤器链(实际运行时)

3.2 各个过滤器被移除的原因对照表
| 被移除的过滤器 | 移除方式 | 为什么移除 |
|---|---|---|
CsrfFilter |
csrf.disable() |
JWT 存在 localStorage 中,不存在 CSRF 风险 |
UsernamePasswordAuthenticationFilter |
formLogin.disable() |
JWT 场景用自定义 /api/auth/login 接口 |
BasicAuthenticationFilter |
httpBasic.disable() |
不需要 HTTP Basic 认证 |
DefaultLoginPageGeneratingFilter |
formLogin.disable() |
前后端分离,不需要生成 HTML 登录页 |
DefaultLogoutPageGeneratingFilter |
logout.disable() |
前后端分离,不需要生成 HTML 登出页 |
RequestCacheAwareFilter |
requestCache.disable() |
无状态模式,不需要缓存原始请求 |
AnonymousAuthenticationFilter |
anonymous.disable() |
JWT 过滤器自己管理未认证状态 |
值得保留的过滤器一共只有 6 个,相比默认配置的 15+ 个过滤器,精简了 60% 以上。这就是"知其所以然"带来的工程收益------你不是在盲目拷贝配置,而是知道每一行配置在做什么。
四、6.x 版本关键变化速查表
Spring Security 6.x 相比 5.x 有一些重要的 API 和默认行为变化,混用会直接编译报错:
| 5.x (旧/已废弃) | 6.x (新) | 说明 |
|---|---|---|
FilterSecurityInterceptor |
AuthorizationFilter |
默认授权过滤器替换,基于新的 AuthorizationManager API |
SecurityContextPersistenceFilter |
SecurityContextHolderFilter |
更轻量的上下文管理,不再负责持久化 |
http.authorizeRequests() |
http.authorizeHttpRequests() |
方法名变化,内部使用新 API,底层从旧的 AccessDecisionManager (基于投票机制) 彻底替换为了新的 AuthorizationManager (基于直接决策)。 |
@EnableGlobalMethodSecurity |
@EnableMethodSecurity |
注解废弃,启用方法安全的新方式 |
链式配置 .and() |
Lambda DSL(唯一) | 6.x 中旧的链式配置已废弃,必须使用 Lambda |
WebSecurityConfigurerAdapter |
基于 Bean 的配置 | 不再需要继承适配器类,直接声明 SecurityFilterChain Bean |
默认使用 AntPathMatcher |
默认使用 PathPatternParser |
PathPatternParser 性能更好,但语法更严格。 |
五、总结
本文是 Spring Security 系列的第一篇。我们从三个层次拆解了过滤器链的完整体系:
| 层次 | 核心知识点 | 一句话总结 |
|---|---|---|
| 定义层 | FilterOrderRegistration 构造方法 |
30+ 过滤器的顺序"宪法",步长 100 预留插入位 |
| 分类层 | 四大功能分类 与对应的开关机制 | 理解每类过滤器的"职责边界"与"启停方式",是做配置决策的基础 |
| 实战层 | JWT 最小过滤器链 | 精简到 6 个过滤器,效率最大化 |
三个最重要的底层认知------记住这些,你就能看懂所有 Spring Security 的配置:
FilterOrderRegistration只定义"顺序",不决定"谁在场" → 过滤器的实际装配由HttpSecurity决定:基础设施层 默认在场,而认证/增强层 则由 DSL 配置(如.formLogin(),.csrf())按需触发加入。ExceptionTranslationFilter排在AuthorizationFilter之前是有意为之 → 它需要"兜底"捕获授权失败抛出的异常,然后根据异常类型(未认证 vs 无权限)做不同处理- JWT 免疫 CSRF 的根本原因是"浏览器不会自动携带
AuthorizationHeader" → 这不是因为 JWT 本身更安全,而是因为它的传输方式不依赖 Cookie 自动携带
下一篇预告 :将深入分析
http.formLogin()一行配置是如何最终变成UsernamePasswordAuthenticationFilter的。内容覆盖:HttpSecurity内部的 Builder 模式设计、Configurer(配置器)的注册与收集机制、doBuild三步曲(init → configure → performBuild)的完整源码拆解,以及AuthenticationManagerBuilder→HttpSecurity→WebSecurity三层构建体系的层级关系。