Spring Security 的配置方式怎么演进的?WebSecurityConfigurerAdapter 为什么被废弃了?
简化版
Spring Security 的配置方式经历了一次大变化:从「继承 WebSecurityConfigurerAdapter 重写方法」变成「定义 SecurityFilterChain Bean」——WebSecurityConfigurerAdapter 在 Spring Security 5.7 被废弃、6.0 被移除。旧方式:写一个类继承 WebSecurityConfigurerAdapter,重写 configure(HttpSecurity)、configure(AuthenticationManagerBuilder) 等方法来配置。新方式:用 @Bean 定义一个 SecurityFilterChain(配置 HttpSecurity 后 return http.build()),用 @Bean 定义 UserDetailsService、PasswordEncoder 等组件。为什么废弃:① 继承的方式不够灵活(一个应用只能有一个 Adapter,难以配置多条过滤器链);② 基于组件(Bean)的方式更符合 Spring 的「组合优于继承」理念——把各配置项声明成 Bean,更清晰、可组合、能定义多个 SecurityFilterChain(按路径匹配不同安全规则)。同时的另一个变化是「Lambda DSL」:配置从链式的 .and() 风格改成 Lambda 风格(http.authorizeHttpRequests(auth -> auth...)),更清晰、不用 .and() 连接。核心:新版用 SecurityFilterChain Bean + 组件 Bean + Lambda DSL,替代旧的继承 Adapter 方式。
详细版
旧方式 vs 新方式:
| 维度 | 旧方式(Adapter) | 新方式(Bean) |
|---|---|---|
| 配置类 | 继承 WebSecurityConfigurerAdapter | 定义 SecurityFilterChain Bean |
| 配置方法 | 重写 configure(HttpSecurity) | @Bean SecurityFilterChain(HttpSecurity) |
| 用户/密码 | 重写 configure(AuthManagerBuilder) | @Bean UserDetailsService/PasswordEncoder |
| 多条链 | 难(一个 Adapter) | 易(多个 SecurityFilterChain Bean,@Order) |
| 风格 | 链式 .and() | Lambda DSL |
| 状态 | 5.7 废弃、6.0 移除 | 现行推荐 |
// ❌ 旧方式(已废弃)
@Configuration
public class OldConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin();
}
}
// ✅ 新方式(Spring Security 5.7+/6)
@Configuration
@EnableWebSecurity
public class NewConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth // Lambda DSL
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build(); // 返回 SecurityFilterChain Bean
}
@Bean
public UserDetailsService userDetailsService() { ... } // 组件也是 Bean
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
⚠️ 理解这个演进的关键,是「从继承到组合」的设计理念转变——
WebSecurityConfigurerAdapter的「继承」方式有个硬伤:一个应用通常只能有一个 Adapter,很难配置「多条过滤器链」(比如「/api/** 用 JWT 无状态、其他用 Session 表单登录」这种「不同路径不同安全规则」的需求,用 Adapter 很别扭)。新的SecurityFilterChainBean 方式,因为是 Bean,可以定义多个——每个SecurityFilterChain用securityMatcher匹配不同的路径、配不同的规则,再用@Order控制优先级,天然支持多条链。这就是「组合优于继承」的体现:把配置声明成可组合的 Bean,比强制继承一个基类更灵活。面试注意:如果还在用WebSecurityConfigurerAdapter,是过时的写法(Spring Boot 3 / Spring Security 6 里已经没有这个类了);新代码要用SecurityFilterChainBean + Lambda DSL。
完整版教学
一、旧方式:继承 WebSecurityConfigurerAdapter
先看旧的配置方式:
旧方式(Spring Security 5.7 之前):
写一个配置类,继承 WebSecurityConfigurerAdapter
重写它的 configure 方法来配置:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 配置 HTTP 安全(授权规则、登录等)
protected void configure(HttpSecurity http) {
http.authorizeRequests()...
}
// 配置认证(用户、密码)
protected void configure(AuthenticationManagerBuilder auth) {
auth.userDetailsService(...).passwordEncoder(...);
}
// 配置 web 安全(忽略静态资源等)
protected void configure(WebSecurity web) { ... }
}
特点:
通过"重写方法"来配置
一个 Adapter 类集中配置所有安全
问题:
① 继承——只能有一个(或有限个)Adapter,难配多条链
② 不够灵活——配置耦合在一个类的方法里
③ 不符合 Spring 的"组合优于继承"理念
所以这是"过时"的方式(5.7 废弃、6.0 移除)
旧方式(Spring Security 5.7 前):写配置类继承 WebSecurityConfigurerAdapter、重写 configure 方法(configure(HttpSecurity) 配授权/登录、configure(AuthenticationManagerBuilder) 配用户/密码、configure(WebSecurity) 忽略静态资源)。特点:通过重写方法配置、一个 Adapter 集中配所有安全。问题:① 继承只能有一个 Adapter 难配多条链、② 不够灵活(配置耦合在一个类)、③ 不符合组合优于继承。所以过时(5.7 废弃、6.0 移除)。理解「旧方式:继承 WebSecurityConfigurerAdapter 重写 configure 方法(HttpSecurity 配授权/AuthManagerBuilder 配用户);问题:继承只能一个难配多条链/不灵活/不符合组合优于继承;5.7 废弃 6.0 移除」,就理解了旧方式和它的问题。
二、新方式:SecurityFilterChain Bean
新方式用 SecurityFilterChain Bean:
新方式(Spring Security 5.7+/6):
不继承任何类,用 @Bean 定义 SecurityFilterChain
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build(); // ★ 返回 SecurityFilterChain
}
@Bean // 用户、密码等组件也是 Bean
public UserDetailsService userDetailsService() { ... }
@Bean
public PasswordEncoder passwordEncoder() { ... }
}
变化点:
① 配置类不再继承 WebSecurityConfigurerAdapter
② 用 @Bean 定义 SecurityFilterChain(配置 HttpSecurity 后 build)
③ UserDetailsService、PasswordEncoder、AuthenticationManager 等
都定义成独立的 @Bean
→ 从"重写方法"变成"定义 Bean"
好处:
① 组合优于继承——各配置项是独立的 Bean,清晰、可组合
② 能定义多个 SecurityFilterChain(多条链)
③ 更符合 Spring 的编程模型(一切皆 Bean)
新方式(5.7+/6):不继承任何类,用 @Bean 定义 SecurityFilterChain(filterChain(HttpSecurity http) 配置后 return http.build()),UserDetailsService/PasswordEncoder/AuthenticationManager 也都定义成独立 @Bean。变化点:① 不再继承 Adapter、② @Bean 定义 SecurityFilterChain、③ 组件都是 Bean(从「重写方法」变「定义 Bean」)。好处:组合优于继承(各配置项独立 Bean 清晰可组合)、能定义多条链、符合 Spring 一切皆 Bean。理解「新方式:不继承、@Bean 定义 SecurityFilterChain(配置 HttpSecurity 后 build)、组件都是独立 Bean;从重写方法变定义 Bean;好处组合优于继承/能多条链/符合 Spring 模型」,就掌握了新方式。
三、Lambda DSL
配置风格也从链式 .and() 变成 Lambda DSL:
旧风格(链式 .and()):
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and() // 用 .and() 连接不同配置块
.formLogin()
.and()
.csrf().disable();
新风格(Lambda DSL):
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.csrf(csrf -> csrf.disable());
变化:
每个配置块用一个 Lambda(配置该块的细节)
→ 不用 .and() 连接(Lambda 自然分隔了各配置块)
→ 更清晰(每个块的配置在自己的 Lambda 里)
方法名变化(配套):
authorizeRequests → authorizeHttpRequests(新的授权配置)
antMatchers → requestMatchers(新的路径匹配)
为什么 Lambda DSL 更好:
① 结构清晰——每个配置块独立(Lambda 内)
② 不用记 .and()(旧方式容易漏 .and() 或搞错层级)
③ 更符合现代 Java 的 Lambda 风格
Customizer.withDefaults():用默认配置该块(如 formLogin 用默认)
所以新配置 = SecurityFilterChain Bean + Lambda DSL
配置风格从链式 .and() 变成 Lambda DSL:旧风格用 .and() 连接不同配置块(authorizeRequests()...and().formLogin()),新风格每个配置块用一个 Lambda(authorizeHttpRequests(auth -> auth...)、formLogin(Customizer.withDefaults())、csrf(csrf -> csrf.disable())),不用 .and() 连接(Lambda 自然分隔)。配套方法名变化:authorizeRequests→authorizeHttpRequests、antMatchers→requestMatchers。好处:结构清晰(每块独立)、不用记 .and()、符合现代 Lambda 风格。理解「Lambda DSL:每个配置块用一个 Lambda 不用.and()连接(旧风格 and 连接易漏)、方法名变化 authorizeHttpRequests/requestMatchers、结构清晰;新配置=SecurityFilterChain Bean+Lambda DSL」,就掌握了 Lambda DSL。
四、多条过滤器链
新方式的一大优势是「支持多条过滤器链」:
需求:不同路径用不同安全规则
/api/** → JWT 无状态认证(STATELESS,不用 Session)
其他 → Session 表单登录
旧方式(Adapter)的难题:
一个 Adapter 难以配置"两套完全不同的安全规则"
(虽然有多个 Adapter 的办法,但别扭)
新方式(多个 SecurityFilterChain Bean):
定义两个 SecurityFilterChain Bean,各配各的:
@Bean @Order(1)
public SecurityFilterChain apiChain(HttpSecurity http) {
http.securityMatcher("/api/**") // 只匹配 /api/**
.authorizeHttpRequests(...)
.sessionManagement(s -> s.sessionCreationPolicy(STATELESS)) // JWT
.addFilter(jwtFilter);
return http.build();
}
@Bean @Order(2)
public SecurityFilterChain webChain(HttpSecurity http) {
http.authorizeHttpRequests(...) // 其他路径
.formLogin(...); // Session 表单登录
return http.build();
}
关键:
① securityMatcher:这条链匹配哪些路径
② @Order:多条链的优先级(先匹配的先用)
→ 请求来了,按 @Order 顺序找第一条 securityMatcher 匹配的链
所以新方式天然支持多条链(各路径不同规则)
→ 这是"组合优于继承"的直接好处
新方式的一大优势是「支持多条过滤器链」——需求如「/api/** 用 JWT 无状态、其他用 Session 表单登录」。旧方式(Adapter)难配两套规则。新方式定义多个 SecurityFilterChain Bean:用 securityMatcher 匹配不同路径、@Order 控制优先级(请求来了按 @Order 找第一条 securityMatcher 匹配的链)。这是「组合优于继承」的直接好处。理解「新方式支持多条链:定义多个 SecurityFilterChain Bean、securityMatcher 匹配不同路径、@Order 控制优先级(请求按 Order 找第一条匹配的链);旧 Adapter 难配多套规则;组合优于继承的好处」,就掌握了多条过滤器链。
五、AuthenticationManager 的获取
新方式下 AuthenticationManager 的获取也变了:
旧方式获取 AuthenticationManager:
重写 authenticationManagerBean() 暴露它
@Override @Bean
public AuthenticationManager authenticationManagerBean() {
return super.authenticationManagerBean();
}
新方式获取 AuthenticationManager:
① 从 AuthenticationConfiguration 拿:
@Bean
public AuthenticationManager authManager(
AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();
}
② 或自己构造 ProviderManager(配置多个 Provider):
@Bean
public AuthenticationManager authManager(...) {
return new ProviderManager(provider1, provider2);
}
什么时候需要 AuthenticationManager:
自定义登录接口(自己调 authenticationManager.authenticate)
自定义认证方式(配置多个 AuthenticationProvider)
→ 需要拿到 AuthenticationManager 来用
其他组件也是 Bean:
UserDetailsService、PasswordEncoder、
AuthenticationProvider、SecurityFilterChain
→ 全部 @Bean 定义、Spring 自动组装
所以新方式一切皆 Bean:配置项都是 Bean、Spring 组装
新方式下 AuthenticationManager 的获取也变了:旧方式重写 authenticationManagerBean();新方式从 AuthenticationConfiguration 拿(config.getAuthenticationManager())或自己构造 ProviderManager(配多个 Provider)。什么时候需要:自定义登录接口(自己调 authenticate)、自定义认证方式(多个 Provider)。其他组件(UserDetailsService/PasswordEncoder/AuthenticationProvider/SecurityFilterChain)也全部 @Bean 定义、Spring 自动组装。理解「新方式获取 AuthenticationManager:从 AuthenticationConfiguration 拿或自己构造 ProviderManager;需要时:自定义登录接口/多个 Provider;新方式一切皆 Bean(组件都@Bean、Spring 组装)」,就掌握了 AuthenticationManager 的获取。
六、迁移与实践
总结从旧到新的迁移和实践:
迁移要点(旧 → 新):
① 配置类去掉 extends WebSecurityConfigurerAdapter
② configure(HttpSecurity) → @Bean SecurityFilterChain(return http.build())
③ configure(AuthManagerBuilder) → @Bean UserDetailsService + PasswordEncoder
④ configure(WebSecurity) 忽略静态资源
→ SecurityFilterChain 里 requestMatchers(...).permitAll()
或 WebSecurityCustomizer Bean
⑤ authorizeRequests → authorizeHttpRequests
antMatchers → requestMatchers
⑥ 链式 .and() → Lambda DSL
⑦ 需要 AuthenticationManager → 从 AuthenticationConfiguration 拿
实践建议:
① 新项目直接用 SecurityFilterChain Bean + Lambda DSL
② 老项目(还用 Adapter)逐步迁移(6.0 已强制)
③ 多路径不同规则 → 多个 SecurityFilterChain + securityMatcher + @Order
④ 组件都定义成 Bean(清晰、可测、可组合)
版本对应:
Spring Security 5.7:废弃 WebSecurityConfigurerAdapter
Spring Security 6.0(Spring Boot 3):移除,只能用新方式
核心总结:
从"继承 WebSecurityConfigurerAdapter 重写方法"
到"定义 SecurityFilterChain Bean + 组件 Bean + Lambda DSL"
原因:组合优于继承(更灵活、支持多条链、符合 Spring 模型)
迁移要点(旧→新):去掉 extends Adapter、configure(HttpSecurity)→@Bean SecurityFilterChain、configure(AuthManagerBuilder)→@Bean UserDetailsService+PasswordEncoder、authorizeRequests→authorizeHttpRequests、antMatchers→requestMatchers、链式 .and()→Lambda DSL、需要 AuthenticationManager 从 AuthenticationConfiguration 拿。实践:新项目直接用新方式、老项目逐步迁移(6.0 强制)、多路径用多个 SecurityFilterChain、组件都定义成 Bean。版本:5.7 废弃、6.0 移除。理解「迁移:去掉继承/configure 变@Bean/方法名变化/and 变 Lambda/AuthManager 从 Config 拿;实践:新项目用新方式/老项目迁移/多路径多条链/组件都 Bean;5.7 废弃 6.0 移除」,就掌握了迁移和实践。
记忆钩子:「Spring Security 配置演进:旧方式(继承 WebSecurityConfigurerAdapter 重写 configure 方法)→新方式(定义 SecurityFilterChain Bean:@Bean filterChain(HttpSecurity)配置后 return http.build()、组件 UserDetailsService/PasswordEncoder 都@Bean);WebSecurityConfigurerAdapter 5.7 废弃 6.0 移除;★为什么废弃:继承方式一个应用只能一个 Adapter 难配多条链、组合优于继承(SecurityFilterChain 是 Bean 可定义多个用 securityMatcher 匹配不同路径+@Order 控制优先级);同时 Lambda DSL:每个配置块用 Lambda 不用.and()连接(authorizeHttpRequests(auth->auth…))、方法名 authorizeRequests→authorizeHttpRequests/antMatchers→requestMatchers;AuthenticationManager 从 AuthenticationConfiguration 拿」。
七、常见误区与追问
- 误区:现在还用 WebSecurityConfigurerAdapter 配置 Spring Security。 已废弃——Spring Security 5.7 废弃、6.0(Spring Boot 3)移除了这个类;新方式是定义 SecurityFilterChain Bean(不继承任何类)+ 组件 Bean + Lambda DSL;用 Adapter 是过时写法,Spring Boot 3 里根本没这个类。
- 误区:SecurityFilterChain 和 WebSecurityConfigurerAdapter 只是名字不同。 理念不同——Adapter 是「继承+重写方法」(一个应用一个 Adapter、难配多条链);SecurityFilterChain 是「定义 Bean」(可定义多个、用 securityMatcher 匹配不同路径、@Order 控制优先级,天然支持多条链);是「组合优于继承」的转变。
- 误区:新旧配置的方法名一样。 有变化——authorizeRequests → authorizeHttpRequests、antMatchers → requestMatchers;还有配置风格从链式 .and() 变成 Lambda DSL(authorizeHttpRequests(auth -> auth…));迁移时要一起改。
- 误区:Lambda DSL 只是写法好看,没实质区别。 有实质好处——每个配置块用独立的 Lambda(配置该块的细节),不用 .and() 连接(旧方式容易漏 .and() 或搞错层级导致配置错误);结构更清晰、每个块的配置在自己的作用域里、不易出错。
- 追问:WebSecurityConfigurerAdapter 为什么被废弃? 主要因为「继承」方式不够灵活——一个应用通常只能有一个 WebSecurityConfigurerAdapter,很难配置多条过滤器链(不同路径用不同安全规则,如 /api/** 用 JWT 无状态、其他用 Session 表单登录);新的 SecurityFilterChain Bean 方式因为是 Bean,可以定义多个,每个用 securityMatcher 匹配不同路径、@Order 控制优先级,天然支持多条链;这是「组合优于继承」理念的体现,把配置声明成可组合的 Bean 比强制继承基类更灵活、更符合 Spring 的编程模型。
- 追问:新方式怎么配置多条过滤器链(不同路径不同规则)? 定义多个 SecurityFilterChain Bean,每个用 securityMatcher(…) 声明它匹配哪些路径、配置各自的安全规则,用 @Order 控制优先级;请求到来时,Spring Security 按 @Order 顺序找第一条 securityMatcher 匹配的链来处理;比如 @Order(1) 的链匹配 /api/**(配 JWT 无状态)、@Order(2) 的链匹配其他(配 Session 表单登录);这是新方式相比旧 Adapter 的一大优势。
- 追问:新方式下怎么获取 AuthenticationManager? 旧方式重写 authenticationManagerBean() 暴露;新方式从 AuthenticationConfiguration 拿——注入 AuthenticationConfiguration,调 config.getAuthenticationManager() 返回并定义成 @Bean;或者自己构造 ProviderManager(传入多个 AuthenticationProvider)定义成 @Bean;需要 AuthenticationManager 的场景是自定义登录接口(自己调 authenticate)或配置多种认证方式(多个 Provider)。
八、加强记忆
Spring Security 配置方式演进:从「继承 WebSecurityConfigurerAdapter 重写方法」变成「定义 SecurityFilterChain Bean」(WebSecurityConfigurerAdapter 5.7 废弃、6.0(Spring Boot 3)移除)。旧方式:写类继承 WebSecurityConfigurerAdapter、重写 configure(HttpSecurity)/configure(AuthenticationManagerBuilder) 等。新方式:用 @Bean 定义 SecurityFilterChain(配置 HttpSecurity 后 return http.build()),UserDetailsService/PasswordEncoder/AuthenticationManager 等都定义成独立 @Bean。为什么废弃:继承方式一个应用只能有一个 Adapter、难配多条链;新的 Bean 方式符合「组合优于继承」——SecurityFilterChain 是 Bean 可定义多个(用 securityMatcher 匹配不同路径、@Order 控制优先级,天然支持多条链,如 /api/** 用 JWT、其他用 Session)。同时的另一个变化是 Lambda DSL:从链式 .and() 改成 Lambda 风格(authorizeHttpRequests(auth -> auth...)),不用 .and() 连接、更清晰;配套方法名变化(authorizeRequests→authorizeHttpRequests、antMatchers→requestMatchers)。AuthenticationManager 从 AuthenticationConfiguration 获取(config.getAuthenticationManager())。一句话「Spring Security 配置从继承 WebSecurityConfigurerAdapter(5.7 废弃 6.0 移除)变成定义 SecurityFilterChain Bean(return http.build())+组件 Bean;废弃因继承难配多条链、组合优于继承(SecurityFilterChain 可定义多个+securityMatcher 匹配路径+@Order);Lambda DSL 替代.and()链式;AuthenticationManager 从 AuthenticationConfiguration 拿」。