比較Spring Security6.X 和 Spring Security 5.X的不同

2023-11-21 12:02:01

   專案使用了SpringBoot3 ,因此 SpringSecurity也相應進行了升級 版本由5.4.5升級到了6.1.5 寫法上發生了很大的變化,最顯著的變化之一就是對 WebSecurityConfigurerAdapter 類的使用方式的改變。這個類在 Spring Security 中被廣泛用於自定義安全設定。以下是主要的差異和寫法上的變化:

  1. 廢棄 WebSecurityConfigurerAdapter

    • 在 Spring Security 5.x 版本中,WebSecurityConfigurerAdapter 是實現安全設定的常用方法。使用者通過繼承這個類,並覆蓋其方法來自定義安全設定。
    • 到了 Spring Security 6.x,WebSecurityConfigurerAdapter 被標記為過時(deprecated),意味著它可能在未來的版本中被移除。這一變化是為了推動使用更現代的設定方法,即使用元件式設定。
  2. 使用元件式設定:

    • 在 Spring Security 6.x 中,推薦使用元件式設定。這意味著你可以建立一個設定類,該類不再需要繼承 WebSecurityConfigurerAdapter
    • 你可以直接定義一個或多個 SecurityFilterChain bean 來設定安全規則。這種方式更加靈活,並且與 Spring Framework 的整體風格更加一致。  

範例程式碼變化:

  • 在 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("${bxt.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 框架的整體設計哲學。