Spring Security 6.x新特性与架构演进详解
定位:第 07 篇,讲透 5→6 的迁移要点、架构演进主线(组件化与 AuthorizationManager)、Lambda DSL 与升级策略
适用版本:Spring Security 6.x(Spring Boot 3.x)
目录
一、5 → 6 迁移要点
1.1 主要破坏性变化
| 变化 | 旧 | 新 |
|---|---|---|
| 配置基类 | 继承 WebSecurityConfigurerAdapter |
移除,改 @Bean SecurityFilterChain |
| 匹配器 | antMatchers / mvcMatchers | 统一 requestMatchers |
| 方法安全注解开关 | @EnableGlobalMethodSecurity | @EnableMethodSecurity |
| URL 授权过滤器 | FilterSecurityInterceptor | AuthorizationFilter |
| 命名空间 | javax | jakarta(随 Spring 6/Boot 3) |
1.2 配置写法对照
java
// 5.x 旧写法
class SecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**").authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")...
}
}
// 6.x 新写法
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasRole("ADMIN")...)
return http.build();
}
1.3 默认行为收紧
6.x 的默认值整体更保守(更安全的默认):部分场景 CSRF 覆盖范围、SecurityContext 保存时机、授权兜底行为都有收紧。升级后必须全量回归安全行为,不能只看编译通过。
二、架构演进主线
2.1 从"继承配置"到"组件化"
旧模型:继承 Adapter,重写 configure 方法(单一配置入口)
新模型:一切皆 Bean------
每个 SecurityFilterChain 是一个 Bean(多链天然支持)
AuthenticationManager、UserDetailsService 等均为可注入 Bean
好处:配置即组装,多链/多认证体系共存顺理成章,测试时可按 Bean 替换。
2.2 AuthorizationManager 统一授权
旧:AccessDecisionManager + Voter(投票器)体系,URL 与方法两套机制
新:AuthorizationManager 统一接口------
URL 级:AuthorizationFilter 调用
方法级:方法安全拦截器调用
统一的判定抽象简化了扩展:自定义授权逻辑实现一个接口即可用于两处。
2.3 SecurityContext 处理显式化
旧:SecurityContextPersistenceFilter 隐式保存
新:显式化------
SecurityContextHolderFilter 只读取(不自动保存)
保存策略可配(如请求结束不自动存,或仅在认证成功后存)
意义:避免"无意中把临时认证写进会话"等问题,行为可预测。
2.4 默认安全持续演进
每个大版本把"更安全的默认"前移:默认开启更多防护、默认拒绝更彻底、弃用不安全特性(密码模式、隐式模式)。跟进版本本身即是安全实践。
三、Lambda DSL
3.1 风格
java
// 链式重载(旧)容易歧义:
http.authorizeRequests().antMatchers("/a").permitAll().and().csrf().disable();
// Lambda(6.x 标准):配置块作用域清晰
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/a").permitAll())
.csrf(csrf -> csrf.disable());
3.2 价值
- 每个配置项的作用域由 Lambda 界定,消除链式调用里"and() 到底属于谁"的歧义;
- 与 Kotlin DSL 风格统一;
- 可读性与可维护性显著提升------新项目一律 Lambda 风格。
四、升级策略
4.1 迁移步骤
① 先升到 5.8(最后的 5.x,内置大量弃用提示与迁移文档)
② 按弃用告警逐项替换:Adapter → Bean、匹配器、方法安全注解
③ 升级到 6:处理编译错误(jakarta 命名空间等)
④ 全量回归:登录流、权限规则、会话、CSRF、响应头
⑤ 核查第三方安全组件(OAuth 组件、自研安全库)的 6.x 兼容性
4.2 回归重点清单
| 项 | 验证 |
|---|---|
| 认证流 | 表单/Basic/自定义登录全部走通 |
| 授权 | 关键角色/权限路径 401/403 行为正确 |
| 会话 | 登录态保持、并发限制、登出 |
| CSRF/头 | 状态变更请求、安全头符合预期 |
| 静态/公开路径 | 放行清单未被默认收紧误伤 |
五、总结
- 迁移要点:Adapter 移除改 Bean、匹配器统一 requestMatchers、方法安全注解更名、jakarta 命名空间、默认行为收紧。
- 演进主线:配置组件化(一切皆 Bean、多链天然)、AuthorizationManager 统一授权、SecurityContext 处理显式化、默认安全持续收紧。
- Lambda DSL:作用域清晰、消除链式歧义,6.x 标准风格。
- 升级策略:5.8 过渡 → 逐项替换 → 6 编译 → 全量回归安全行为 + 第三方组件核查。
六、常见高频面试题
1. Spring Security 5 升级到 6 的主要工作有哪些?
要点:① 移除 WebSecurityConfigurerAdapter,改为 @Bean SecurityFilterChain 组件化配置;② 匹配器统一:antMatchers/mvcMatchers → requestMatchers;③ 方法安全:@EnableGlobalMethodSecurity → @EnableMethodSecurity;④ 命名空间随 Spring 6/Boot 3 迁到 jakarta;⑤ 默认行为收紧需全量回归(授权兜底、CSRF、头);⑥ 第三方安全组件兼容性核查。建议先经 5.8 过渡处理弃用告警。
2. WebSecurityConfigurerAdapter 为什么被移除?新写法是什么?
要点:旧模型靠继承基类重写 configure 方法,配置与类继承耦合、多链表达困难、不易测试。新模型组件化:每个 SecurityFilterChain 声明为一个 @Bean,用 HttpSecurity 的 Lambda DSL 构建规则;认证相关组件(UserDetailsService、AuthenticationManager)也都是普通 Bean。好处:多安全链天然共存、按 Bean 组装与替换、测试友好。这是"继承配置 → 声明式组件"的架构转向。
3. AuthorizationManager 统一了什么?
要点:6.x 用 AuthorizationManager 统一了 URL 级与方法级两套授权判定机制(旧版分别是 AccessDecisionManager/Voter 体系与方法拦截器,模型不一致)。统一接口:接收认证与受保护对象,返回授权决定。URL 级由 AuthorizationFilter 调用,方法级由方法安全拦截调用。价值:自定义授权逻辑一个实现两处可用,扩展模型简化,也便于审计与测试。
4. Lambda DSL 相比链式调用好在哪?
要点:链式写法里 authorizeRequests().xxx().and().csrf()... 的 and() 归属容易混淆,配置项的作用域靠读者自行解析;Lambda 写法每个配置块有明确作用域(auth -> ...、csrf -> ...),结构即语义,消除歧义、可读性强,且与 Kotlin DSL 风格一致。6.x 全面 Lambda 化,新项目应直接使用,旧代码升级时一并重构配置块。
5. SecurityContext 的保存在 6.x 有什么变化?
要点:旧版 SecurityContextPersistenceFilter 会在请求结束隐式保存上下文到会话,可能把临时认证状态意外持久化。6.x 拆分为 SecurityContextHolderFilter(只读取恢复,不自动保存)+ 显式保存策略(如 SecurityContextRepository 在认证成功后保存、或按需配置请求结束保存)。行为更可预测,避免"未登录动作污染会话登录态"类问题,也便于无状态场景。
6. requestMatchers 和旧的 antMatchers 有什么区别?
要点:requestMatchers 是 6.x 的统一匹配入口,整合了路径匹配(Ant 风格)与 MVC 匹配能力,并根据应用类型(Servlet/MVC 存在与否)自动选择实现;antMatchers/mvcMatchers 是旧的两个分散 API。使用上语法兼容(仍支持 /api/** 等 Ant 风格),并可带 HttpMethod 重载。升级时机械替换即可,但要注意个别边界行为差异,配合测试验证。
7. 为什么安全框架升级必须做行为回归而不只是编译通过?
要点:安全框架大量行为是运行时默认值驱动的(默认拦截、默认头、默认兜底规则),版本升级可能改变默认行为而编译完全正常。典型风险:默认收紧导致原本放行的路径变 403、CSRF 覆盖变化导致表单提交失败、会话保存时机变化导致登录态异常。所以必须回归:认证流、授权矩阵、会话、静态/公开路径、状态变更请求,用真实请求验证而非只跑单测。
8. 多套安全策略(表单后台 + JWT API)在 6.x 如何组织?
要点:组件化模型天然支持:声明多个 @Bean SecurityFilterChain,各自用 securityMatcher 划定作用路径(如 /api/** 与 /admin/**),用 @Order 决定匹配优先级;请求按序匹配第一条命中的链执行。API 链配 STATELESS + JWT 过滤器 + 关 CSRF,后台链配表单登录与会话。旧模型下这种混用很别扭,组件化后是标准用法。
9. 升级后出现原本正常的接口 403,如何排查?
要点:按默认行为收紧方向排查:① 是否命中了新的授权兜底(未明确放行的路径被拒)------检查放行清单与规则顺序;② CSRF 覆盖是否变化(状态变更请求缺 Token);③ 匹配器语义差异(路径匹配范围变化);④ SecurityContext 保存时机变化导致登录态丢失;⑤ 安全头/会话固定策略变化。用 403 响应的具体类型(授权/CSRF)与调试日志定位,再逐项修复或显式配置。
10. 自研系统用了大量安全扩展,升级 6 要注意什么?
要点:① 自定义过滤器:检查引用的 API 是否重命名/移除(如 Filter 基类、SecurityContext 处理类),适配新模型;② 自定义授权逻辑:从 Voter/AccessDecisionManager 迁移到 AuthorizationManager;③ 自研认证组件:Authentication 构造与 Provider 接口变化;④ 依赖的第三方安全库(OAuth 组件、验证码等)确认支持 6.x;⑤ 全面测试自定义链路的认证与授权。建议在 5.8 阶段就按弃用提示完成大部分改造。
