專案使用了SpringBoot3 ,因此 SpringSecurity也相應進行了升級 版本由5.4.5升級到了6.1.5 寫法上發生了很大的變化,最顯著的變化之一就是對 WebSecurityConfigurerAdapter
類的使用方式的改變。這個類在 Spring Security 中被廣泛用於自定義安全配置。以下是主要的差異和寫法上的變化:
-
廢棄
WebSecurityConfigurerAdapter
:- 在 Spring Security 5.x 版本中,
WebSecurityConfigurerAdapter
是實現安全配置的常用方法。使用者透過繼承這個類,並覆蓋其方法來自定義安全配置。 - 到了 Spring Security 6.x,
WebSecurityConfigurerAdapter
被標記為過時(deprecated),意味著它可能在未來的版本中被移除。這一變化是為了推動使用更現代的配置方法,即使用元件式配置。
- 在 Spring Security 5.x 版本中,
-
使用元件式配置:
- 在 Spring Security 6.x 中,推薦使用元件式配置。這意味著你可以建立一個配置類,該類不再需要繼承
WebSecurityConfigurerAdapter
。 - 你可以直接定義一個或多個
SecurityFilterChain
bean 來配置安全規則。這種方式更加靈活,並且與 Spring Framework 的整體風格更加一致。
- 在 Spring Security 6.x 中,推薦使用元件式配置。這意味著你可以建立一個配置類,該類不再需要繼承
示例程式碼變化:
- 在 Spring Security 5.x 使用
WebSecurityConfigurerAdapter
的配置框架:
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); } }
- 在 Spring Security 6.x 使用元件式配置變成了這個樣子:
@Configuration public class SecurityConfiguration { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); return http.build(); } }
專案上具體的寫法:
- 在 Spring Security 5.4.5 使用
WebSecurityConfigurerAdapter
的配置:
@Configuration @Component @EnableWebSecurity @AllArgsConstructor @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Value("${auth.whitelist:/login}") private final String[] URL_WHITELIST; private final AuthenticationFailureHandler loginFailureHandler; private final UserLoginSuccessHandler loginSuccessHandler; private final UserLogoutSuccessHandler logoutSuccessHandler; private final UserDetailsService userDetailsService; private final AccessDeniedHandler authAccessDeniedHandler; private final AuthenticationEntryPoint loginAuthenticationEntryPoint; @Bean BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Bean public UserAuthenticationProvider myAuthenticationProvider() { UserAuthenticationProvider userAuthenticationProvider = new UserAuthenticationProvider(userDetailsService); return userAuthenticationProvider; } @Bean JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception { JwtAuthenticationFilter jwtAuthenticationFilter2 = new JwtAuthenticationFilter(authenticationManager()); return jwtAuthenticationFilter2; } @Override protected void configure(HttpSecurity http) throws Exception { http .cors() .and() .csrf().disable() .formLogin() .loginProcessingUrl("/login") .usernameParameter("userName") .passwordParameter("password") .successHandler(loginSuccessHandler) .failureHandler(loginFailureHandler) .and() .logout() .logoutSuccessHandler(logoutSuccessHandler) //禁用session .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //配置攔截規則 .and() .authorizeRequests() .antMatchers(URL_WHITELIST).permitAll() .anyRequest().authenticated() //異常處理器 .and() .exceptionHandling() .authenticationEntryPoint(loginAuthenticationEntryPoint) .accessDeniedHandler(authAccessDeniedHandler) .and() .addFilterAfter(jwtAuthenticationFilter(), ExceptionTranslationFilter.class); } }
- 使用SpringSecurity6.1.5實現相同邏輯的配置變成了如下形式:
@Configuration @Component @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfiguration { @Value("${auth.whitelist:/login}") private final String[] URL_WHITELIST; private final CustomerUserDetailsService customUserDetailsService; private final CustomLoginSuccessHandler loginSuccessHandler; private final CustomLoginFailureHandler loginFailureHandler; private final AccessDeniedHandler authAccessDeniedHandler; private final AuthenticationEntryPoint loginAuthenticationEntryPoint; @Bean BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); // 例項化您的 JWT 過濾器 } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return new ProviderManager(new DaoAuthenticationProvider() {{ setUserDetailsService(customUserDetailsService); setPasswordEncoder(bCryptPasswordEncoder()); }}); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeRequests(authz -> authz .requestMatchers(URL_WHITELIST).permitAll() // 允許訪問無需認證的路徑 .anyRequest().authenticated() ) .formLogin(form -> form. loginProcessingUrl("/login") .usernameParameter("username") .passwordParameter("password") .successHandler(loginSuccessHandler) .failureHandler(loginFailureHandler) ) .logout(Customizer.withDefaults()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .userDetailsService(customUserDetailsService) .exceptionHandling(exceptionHandle -> exceptionHandle .authenticationEntryPoint(loginAuthenticationEntryPoint) .accessDeniedHandler(authAccessDeniedHandler) ) .addFilterAfter(jwtAuthenticationFilter(), ExceptionTranslationFilter.class) .httpBasic(Customizer.withDefaults()); return http.build(); } }
總之,從 Spring Security 5.x 遷移到 6.x,主要的改變是從繼承 WebSecurityConfigurerAdapter
轉向定義 SecurityFilterChain
bean 的方式來配置安全性。這種變化旨在使配置更加模組化和靈活,並更好地符合 Spring 框架的整體設計哲學。