Spring Security 过滤器链全景图:从 FilterOrderRegistration 到实战配置

0 阅读17分钟

深入理解 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 过滤器,却发现它"不生效"——很可能只是放错了位置!

本文将围绕三个核心问题展开:

  1. "宪法"问题:Spring Security 如何定义 30+ 过滤器的严格执行顺序?
  2. "开关"问题:哪些过滤器是"天生就在的"(核心骨架),哪些是"Spring Boot 帮你开的"(自动配置),哪些必须"你自己动手"(手动开启)?
  3. "取舍"问题:在前后端分离 + JWT 的场景下,哪些过滤器成"累赘"需要关掉?

一、FilterOrderRegistration:过滤器顺序的"宪法"

1.1 什么是 FilterOrderRegistration?

FilterOrderRegistration 是 Spring Security 内部的一个注册表类,它定义了所有安全过滤器的默认执行顺序。可以把它理解为过滤器链的"宪法"——每个过滤器在该注册表中都被分配了一个整数值,数值越小,优先级越高,越先执行。

1.2 完整源码解析

FilterOrderRegistration 的核心逻辑在其构造方法中——一个 Step 对象,每次调用 next() 增加 ORDER_STEP(默认 100)。注意:FilterOrderRegistration 只是定义了顺序,并不代表所有过滤器都开启了——它的角色是"宪法",而不是"执行名单"。

以下是框架内预定义的所有过滤器及其顺序

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=100INITIAL_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 存储filterToOrderHashMap<String, Integer>Key 是过滤器类名或全限定名,Value 是整数序号。序号越小,越先执行

1.3 从"顺序注册表"到"实际执行顺序"的完整对照表

"顺序注册表"是全局的、静态的排序参考系;"实际执行顺序"是局部的、动态的、经过筛选后按该参考系排列的最终结果。

序号过滤器类别职责一句话
1DisableEncodeUrlFilter预处理禁用 Servlet 响应的 URL 编码,主要目的是防止 Session ID 被嵌入 URL 导致泄露
2ForceEagerSessionCreationFilter预处理强制提前创建 Session
3ChannelProcessingFilter通道安全HTTP → HTTPS 强制跳转
4HttpsRedirectFilter通道安全HTTPS 重定向
5WebAsyncManagerIntegrationFilter上下文SecurityContext 传播到 Spring MVC 的异步请求处理中
6SecurityContextHolderFilter上下文6.x 核心:请求开始加载 SecurityContext
7SecurityContextPersistenceFilter上下文5.x 兼容:请求开始加载 + 结束保存 SecurityContext
8HeaderWriterFilter基础安全写入 X-Frame-Options、HSTS 等安全响应头
9CorsFilter基础安全处理跨域资源共享(需手动 http.cors() 开启)
10CsrfFilter基础安全CSRF Token 校验(默认开启)
11LogoutFilter基础安全/logout 请求处理:销毁 Session
12OAuth2AuthorizationRequestRedirectFilterOAuth2OAuth2 登录入口重定向
13X509AuthenticationFilter认证X.509 证书认证
14UsernamePasswordAuthenticationFilter认证★表单登录:POST /login → 账号密码认证
15DefaultLoginPageGeneratingFilter页面生成如果未自定义登录页,生成默认 HTML 登录表单
16DefaultLogoutPageGeneratingFilter页面生成如果未自定义登出页,生成默认登出确认页
17ConcurrentSessionFilter会话并发会话控制(同一账号多端登录限制)
18BearerTokenAuthenticationFilter认证OAuth2 Resource Server:Bearer Token 认证
19BasicAuthenticationFilter认证HTTP Basic Auth:请求头 Authorization: Basic xxx
20RequestCacheAwareFilter缓存检查是否有被缓存的原始请求(如登录前的 URL),如有则重定向到缓存的 URL
21SecurityContextHolderAwareRequestFilter适配包装 request,提供 Servlet 3.0 安全 API
22RememberMeAuthenticationFilter认证"记住我"Cookie 自动登录
23AnonymousAuthenticationFilter认证给未登录用户一个"匿名身份"
24SessionManagementFilter会话会话固定攻击防护 + 并发控制
25ExceptionTranslationFilter核心★捕获 AuthenticationException / AccessDeniedException
26AuthorizationFilter授权★6.x 默认授权:检查用户是否有权访问该 URL
27SwitchUserFilter调试管理员模拟其他用户登录(ROLE_PREVIOUS_ADMINISTRATOR)

重要认知:从源码的 FilterOrderRegistration 能看出来,认证相关的过滤器统一排在授权之前(第 6-24 位),异常处理(第 25 位)紧挨着授权过滤器(第 26 位)。这个顺序是精心设计的:先搞清楚你是谁(认证),才能判断你能做什么(授权),异常处理器紧跟其后,一旦授权拒绝立刻捕获并转为 HTTP 响应。


二、核心过滤器深度拆解

Spring Security 的过滤器可按照功能职责以分为四大类,理解这个分类是后续所有实战决策的基础:

2.1 第一类:基础设施层

构成安全框架的底层骨架,通常由 HttpSecurity 默认构建,极难也不建议禁用。它们不直接处理业务认证,而是为整个链条提供运行环境。

SecurityContextHolderFilter (6.x 核心)

核心源码行为

请求进入时 → 从 SecurityContextRepository 加载 SecurityContext → 设置到 SecurityContextHolder
请求结束时 → SecurityContextHolder.clearContext() 清理线程变量

6.x 关键变化

  • SecurityContextPersistenceFilter(旧版):负责加载保存SecurityContext,请求结束时会调用repository.saveContext()
  • SecurityContextHolderFilter(6.x 新增):仅负责从 SecurityContextRepository 加载SecurityContext 到当前线程,并在请求结束时清理线程变量。更轻量,更符合"单一职责"

实战影响:如果你使用 JWT 无状态方案,可以显式设置 SecurityContextRepositoryNullSecurityContextRepository,避免任何不必要的 Session 操作:

http.securityContext(context -> context
    .securityContextRepository(new NullSecurityContextRepository()));

② ExceptionTranslationFilter

这是整个过滤器链中"最智能"的过滤器,但很多人不理解它的价值。

它位于 AuthorizationFilter 之前(注意:这是关键!),意味着它能捕获后续过滤器抛出的异常:

// 伪代码: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);
}

两种异常的区分逻辑

  1. AuthenticationException:用户未登录 → 保存当前请求到 Cache,重定向到 /login
  2. AccessDeniedException:用户已登录但权限不足 → 返回 403 Forbidden(不会重定向到登录页,因为重定向了也没用——用户已经登录了,只是没权限)

③ AuthorizationFilter(6.x 默认)

位于 ExceptionTranslationFilter 之后SwitchUserFilter 之前。它是业务请求到达 Controller 前的最后一道安全关卡(但并非物理链尾)。

核心逻辑极为简洁:

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 后自动开启。

下一篇会完整拆解这个过滤器从 attemptAuthenticationSecurityContext 保存的全链路源码。

② BasicAuthenticationFilter

维度说明
触发原因HttpSecurity 默认启用(非 Boot 特有);Boot 项目中未显式 disable 时自动生效
触发条件请求头包含 Authorization: Basic <Base64(username:password)>
核心动作提取 Header → Base64 解码 → 封装 Token → 调用 AuthenticationManager.authenticate()
典型场景Postman 测试、微服务间调用、curl 命令行
关闭方式http.httpBasic(basic -> basic.disable())

③ AnonymousAuthenticationFilter

很多人不理解为什么要给"没登录的人"一个身份。 这其实是一个防止空指针的优雅设计。

// 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 时:

  1. ExceptionTranslationFilter 将当前请求保存到 RequestCache
  2. 用户被重定向到登录页
  3. 登录成功后,RequestCacheAwareFilter 从 Cache 中取出原始请求并重放
  4. 用户直接看到登录前想看的页面(而不是被扔到首页)

2.3 第三类:安全增强层:防御策略的实施者

提供 CSRF、CORS、安全头等横切面防护。大多数可按需禁用,但禁用前必须评估安全风险

① CsrfFilter:前后端分离最常被误解的过滤器

这是整个 Spring Security 中最容易被误解、也最常被误关或误开的过滤器。让我们仔细讲清楚。

CSRF 攻击的本质

  1. 用户登录了 bank.com,浏览器存了 Session Cookie
  2. 用户访问了恶意网站 evil.com
  3. 恶意网站向 bank.com/transfer?to=hacker&amount=10000 发送请求
  4. 浏览器自动带上了 bank.com 的 Cookie! → 请求被服务器认为是合法的 → 钱被转走

CsrfFilter 的防御原理

  1. 服务器生成一个随机 Token,存入 Session
  2. 将这个 Token 以隐藏字段的形式嵌入表单
  3. 表单提交时,服务器校验表单中的 Token 与 Session 中的 Token 是否一致
  4. 恶意网站无法读取 bank.com 页面中的 Token(同源策略),所以攻击失败

为什么 JWT 场景不需要 CSRF 防护?

这是最关键的认知:

维度Session 模式JWT 模式
认证凭证位置Cookie(JSESSIONID客户端 localStorage 或内存
凭证发送方式浏览器自动携带前端手动设置 Authorization Header
CSRF 攻击可行性✅ 可行:跨站请求会自动带 Cookie❌ 不可行:跨站请求不会自动带 Authorization Header
是否需要 CsrfFilter需要不需要

JWT 免疫 CSRF 的底层原因

  • 浏览器不会在跨站请求中自动添加 Authorization
  • 跨站脚本无法读取目标域的 localStorage(受同源策略保护)
  • 因此,攻击者无法伪造携带有效 JWT 的请求 → CSRF 攻击失败
// JWT 项目标配:关闭 CSRF
http.csrf(csrf -> csrf.disable());

⚠️ 警告:如果你把 JWT 存到了 Cookie 中(而不是 localStorage),那么 CSRF 风险依然存在!JWT 的 CSRF 免疫建立在"客户端主动设置请求头"这个前提之上。

② CorsFilter:跨域资源共享

CorsFilterFilterOrderRegistration 中有注册顺序,但默认不会自动加入过滤器链,需要手动开启:

http.cors(Customizer.withDefaults());

同时必须提供 CorsConfigurationSource Bean:

// 方式一:使用全局 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-Optionsnosniff阻止浏览器 MIME 类型嗅探
X-Frame-OptionsDENY防止页面被嵌入 iframe(点击劫持)
X-XSS-Protection0禁用浏览器过时的 XSS 过滤器(避免副作用)
Strict-Transport-Securitymax-age=31536000强制 HTTPS(仅 HTTPS 请求)
Cache-Controlno-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 场景的标准配置如下。注意每一行配置背后对应着哪些过滤器的开启/关闭

@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 精简后的过滤器链(实际运行时)

01-jwt-minimal-chain.png

3.2 各个过滤器被移除的原因对照表

被移除的过滤器移除方式为什么移除
CsrfFiltercsrf.disable()JWT 存在 localStorage 中,不存在 CSRF 风险
UsernamePasswordAuthenticationFilterformLogin.disable()JWT 场景用自定义 /api/auth/login 接口
BasicAuthenticationFilterhttpBasic.disable()不需要 HTTP Basic 认证
DefaultLoginPageGeneratingFilterformLogin.disable()前后端分离,不需要生成 HTML 登录页
DefaultLogoutPageGeneratingFilterlogout.disable()前后端分离,不需要生成 HTML 登出页
RequestCacheAwareFilterrequestCache.disable()无状态模式,不需要缓存原始请求
AnonymousAuthenticationFilteranonymous.disable()JWT 过滤器自己管理未认证状态

值得保留的过滤器一共只有 6 个,相比默认配置的 15+ 个过滤器,精简了 60% 以上。这就是"知其所以然"带来的工程收益——你不是在盲目拷贝配置,而是知道每一行配置在做什么。


四、6.x 版本关键变化速查表

Spring Security 6.x 相比 5.x 有一些重要的 API 和默认行为变化,混用会直接编译报错:

5.x (旧/已废弃)6.x (新)说明
FilterSecurityInterceptorAuthorizationFilter默认授权过滤器替换,基于新的 AuthorizationManager API
SecurityContextPersistenceFilterSecurityContextHolderFilter更轻量的上下文管理,不再负责持久化
http.authorizeRequests()http.authorizeHttpRequests()方法名变化,内部使用新 API,底层从旧的 AccessDecisionManager (基于投票机制) 彻底替换为了新的 AuthorizationManager (基于直接决策)。
@EnableGlobalMethodSecurity@EnableMethodSecurity注解废弃,启用方法安全的新方式
链式配置 .and()Lambda DSL(唯一)6.x 中旧的链式配置已废弃,必须使用 Lambda
WebSecurityConfigurerAdapter基于 Bean 的配置不再需要继承适配器类,直接声明 SecurityFilterChain Bean
默认使用 AntPathMatcher默认使用 PathPatternParserPathPatternParser 性能更好,但语法更严格。

五、总结

本文是 Spring Security 系列的第一篇。我们从三个层次拆解了过滤器链的完整体系:

层次核心知识点一句话总结
定义层FilterOrderRegistration 构造方法30+ 过滤器的顺序"宪法",步长 100 预留插入位
分类层四大功能分类与对应的开关机制理解每类过滤器的"职责边界"与"启停方式",是做配置决策的基础
实战层JWT 最小过滤器链精简到 6 个过滤器,效率最大化

三个最重要的底层认知——记住这些,你就能看懂所有 Spring Security 的配置

  1. FilterOrderRegistration 只定义"顺序",不决定"谁在场" → 过滤器的实际装配由 HttpSecurity 决定:基础设施层默认在场,而认证/增强层则由 DSL 配置(如 .formLogin(), .csrf())按需触发加入。
  2. ExceptionTranslationFilter 排在 AuthorizationFilter 之前是有意为之 → 它需要"兜底"捕获授权失败抛出的异常,然后根据异常类型(未认证 vs 无权限)做不同处理
  3. JWT 免疫 CSRF 的根本原因是"浏览器不会自动携带 Authorization Header" → 这不是因为 JWT 本身更安全,而是因为它的传输方式不依赖 Cookie 自动携带

下一篇预告:将深入分析 http.formLogin() 一行配置是如何最终变成 UsernamePasswordAuthenticationFilter 的。内容覆盖:HttpSecurity 内部的 Builder 模式设计、Configurer(配置器)的注册与收集机制、doBuild 三步曲(init → configure → performBuild)的完整源码拆解,以及 AuthenticationManagerBuilderHttpSecurityWebSecurity 三层构建体系的层级关系。