對于許多經驗豐富的 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 生態發展的正確路徑。