对于许多经验丰富的 Spring开发者来说,WebSecurityConfigurerAdapter
是一个再熟悉不过的名字。在很长一段时间里,它几乎是所有 Spring Security 配置的起点和核心。然而,随着 Spring Boot 3.x 和 Spring Security 6.x 的普及,这个曾经的“老朋友”已经退出了历史舞台。
那么,我们为什么曾经需要覆盖 WebSecurityConfigurerAdapter
?如今又为什么不再需要(甚至不能)这样做?理解这个演变过程,能帮助我们更好地掌握现代 Spring Security 的配置精髓。
“过去时”:为什么要覆盖 WebSecurityConfigurerAdapter
?
在 Spring Security 5.7 版本之前,WebSecurityConfigurerAdapter
是官方推荐的配置入口。它是一个抽象类,通过继承并覆盖其内部的 configure
方法,开发者可以方便地自定义应用程序的安全策略。
选择覆盖它主要有以下三个原因,对应其三个核心的 configure
方法:
1. 配置 HTTP 安全策略 (configure(HttpSecurity http)
)
这是最常用、最重要的一个方法。覆盖它,是为了定义哪些 URL 路径需要被保护,以及如何保护。
-
• 用途:
-
• 配置 URL 的访问权限(如
antMatchers
,permitAll
,authenticated
)。 -
• 配置登录方式(如表单登录
formLogin()
、HTTP Basic 认证)。 -
• 配置登出行为 (
logout()
)。 -
• 配置 CSRF(跨站请求伪造)保护。
-
• 配置 CORS(跨域资源共享)。
-
• 管理会话(Session Management)。
-
- • 经典示例代码 (旧方式):
@Configuration @EnableWebSecurity publicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception {http.authorizeRequests().antMatchers("/public/**", "/login").permitAll() // 公开路径允许所有访问.anyRequest().authenticated() // 其他所有请求都需要认证.and().formLogin() // 启用表单登录.loginPage("/login").defaultSuccessUrl("/dashboard").and().logout() // 启用登出.logoutSuccessUrl("/login?logout");} }
2. 配置认证管理器 (configure(AuthenticationManagerBuilder auth)
)
这个方法用于构建和配置**用户认证(Authentication)**的方式,即回答“用户是谁,以及如何验证他们的身份”。
-
• 用途:
-
• 配置内存中的用户 (
inMemoryAuthentication
),常用于开发和测试。 -
• 配置基于数据库的用户 (
jdbcAuthentication
)。 -
• 集成自定义的
UserDetailsService
,从任何数据源(如数据库、LDAP、外部API)加载用户信息。 -
• 指定密码编码器 (
PasswordEncoder
),如BCryptPasswordEncoder
。
-
- • 经典示例代码 (旧方式):
@Configuration // ... 其他注解 publicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService myUserDetailsService;@Beanpublic PasswordEncoder passwordEncoder() {returnnewBCryptPasswordEncoder();}@Overrideprotectedvoidconfigure(AuthenticationManagerBuilder auth)throws Exception {auth.userDetailsService(myUserDetailsService) // 使用自定义的 UserDetailsService.passwordEncoder(passwordEncoder()); // 设置密码编码器}// ... configure(HttpSecurity http) 方法 }
3. 配置全局 Web 安全 (configure(WebSecurity web)
)
此方法用于配置全局性的安全设置,通常是那些完全独立于核心安全过滤器链的资源,例如静态资源。
-
• 用途:
-
• 设置需要被安全过滤器忽略的路径,如
/css/**
,/js/**
,/images/**
。这可以提升性能,因为对这些资源的请求将不会经过 Spring Security 的复杂处理流程。
-
- • 经典示例代码 (旧方式):
@Configuration // ... 其他注解 publicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {@Overridepublicvoidconfigure(WebSecurity web)throws Exception {web.ignoring().antMatchers("/css/**", "/js/**", "/images/**");}// ... 其他 configure 方法 }
“现在时”:为什么不再使用 WebSecurityConfigurerAdapter
?
核心原因:WebSecurityConfigurerAdapter
自 Spring Security 5.7 起被标记为@Deprecated(过时),并在 Spring Security 6.0 (对应 Spring Boot 3.0) 中被完全移除。
Spring 团队做出这个决定,是为了推动一种更现代化、更灵活的基于组件(Component-based)的配置方式。
-
• 拥抱依赖注入和 IoC:新的方式不再依赖继承和方法覆盖,而是鼓励开发者将安全配置定义为独立的 Spring Bean。这更符合 Spring 的核心思想——控制反转(IoC)。
-
• 配置解耦:每个安全相关的配置(如
SecurityFilterChain
,UserDetailsService
,PasswordEncoder
)都是一个独立的 Bean,职责更单一,更易于测试和管理。 -
• 提升清晰度和可维护性:通过显式定义 Bean,配置的来源和作用一目了然,减少了因继承带来的“隐式”行为。
现代 Spring Security 配置方式 (The New Way)
那么,如何用现代的方式实现与上述三个 configure
方法相同的效果呢?
1. 替代 configure(HttpSecurity http)
-> 定义 SecurityFilterChain
Bean
这是最核心的变化。现在,我们通过定义一个 SecurityFilterChain
类型的 Bean 来配置 HTTP 安全。
- • 现代示例代码:
注意:新的配置风格大量采用 Lambda DSL,代码更紧凑、更具可读性。@Configuration @EnableWebSecurity publicclassModernSecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception {http.authorizeHttpRequests(authorize -> authorize.requestMatchers("/public/**", "/login").permitAll() // 使用 Lambda DSL 配置.anyRequest().authenticated()).formLogin(formLogin -> formLogin.loginPage("/login").defaultSuccessUrl("/dashboard")).logout(logout -> logout.logoutSuccessUrl("/login?logout"));return http.build();} }
2. 替代 configure(AuthenticationManagerBuilder auth)
-> 定义独立的认证组件 Bean
不再需要 AuthenticationManagerBuilder
。Spring Security 会自动发现并使用我们定义的 UserDetailsService
和 PasswordEncoder
Bean。
- • 现代示例代码:
@Configuration publicclassModernSecurityConfig {@Beanpublic UserDetailsService userDetailsService() {// 以内存用户为例UserDetailsuser= User.builder().username("user").password(passwordEncoder().encode("password")).roles("USER").build();returnnewInMemoryUserDetailsManager(user);}@Beanpublic PasswordEncoder passwordEncoder() {returnnewBCryptPasswordEncoder();}// ... SecurityFilterChain Bean }
3. 替代 configure(WebSecurity web)
-> 定义 WebSecurityCustomizer
Bean
为了忽略静态资源,我们现在定义一个 WebSecurityCustomizer
类型的 Bean。
- • 现代示例代码:
@Configuration public class ModernSecurityConfig {@Beanpublic WebSecurityCustomizer webSecurityCustomizer() {return (web) -> web.ignoring().requestMatchers("/css/**", "/js/**", "/images/**");}// ... 其他安全相关的 Bean }
新旧对比总结
任务 | 旧方式 ( | 新方式 (Component-based Beans) |
HTTP 安全配置 | 覆盖 | 定义 |
用户认证配置 | 覆盖 | 定义 |
忽略静态资源 | 覆盖 | 定义 |
结论
WebSecurityConfigurerAdapter
曾在 Spring Security 的发展中扮演了至关重要的角色,它提供了一种直观、集中的方式来管理安全配置。然而,技术的演进使其让位于一种更灵活、解耦且符合 Spring 核心理念的组件化配置模型。
对于正在维护旧项目(Spring Boot 2.x)的开发者来说,理解 WebSecurityConfigurerAdapter
的工作原理仍然是有价值的。但对于所有新项目以及计划升级到 Spring Boot 3.x 的项目而言,拥抱基于 Bean 的配置方式是必然的选择。这种现代化的配置不仅能让你的代码更清晰、更易于维护,也是跟上 Spring 生态发展的正确路径。