如何從Spring Security 5遷移到Spring Security 6/Spring Boot 3

banq發表於2024-03-12

Spring Security 6 帶來了幾項重大更改,包括刪除類和已棄用的方法,以及引入新方法。

從 Spring Security 5 遷移到 Spring Security 6 可以增量完成,而不會破壞現有的程式碼庫。另外,我們可以使用OpenRewrite等第三方外掛來方便遷移到最新版本。

在本教程中,我們將學習如何使用 Spring Security 5 將現有應用程式遷移到 Spring Security 6。我們將替換已棄用的方法並利用lambda DSL 來簡化配置。此外,我們將利用 OpenRewrite 來加快遷移速度。

Spring Security 和 Spring Boot 版本
Spring Boot基於Spring框架,Spring Boot的版本使用最新版本的Spring框架。Spring Boot 2 預設使用 Spring Security 5,而 Spring Boot 3 使用 Spring Security 6。

要在 Spring Boot 應用程式中使用 Spring Security,我們始終將spring-boot-starter-security 依賴項新增到pom.xml 中。

但是,我們可以透過在pom.xml的屬性 部分指定所需的版本來覆蓋預設的 Spring Security 版本:

<properties>
    <spring-security.version>5.8.9</spring-security.version>
</properties>

在這裡,我們指定在專案中使用 Spring Security 5.8.9,覆蓋預設版本。

值得注意的是,我們還可以透過覆蓋屬性部分中的預設版本來在 Spring Boot 2 中使用 Spring Security 6 。

Spring Security 6 的新增功能
Spring Security 6 引入了多項功能更新以提高安全性和穩健性。現在它至少需要 Java 版本 17 並使用jakarta名稱空間。

主要變化之一是刪除了WebSecurityConfigurerAdapter,轉而採用基於元件的安全配置。

此外,authorizeRequests()被刪除並替換為authorizeHttpRequests()來定義授權規則。

此外,它還引入了requestMatcher()和securityMatcher()等方法來替代antMatcher()和 m vcMatcher()來配置請求資源的安全性。requestMatcher ()方法更安全,因為它為請求配置選擇適當的RequestMatcher 實現。

其他已棄用的方法(如cors()和csrf())現在有函式式風格的替代方法。

專案設定
首先,讓我們透過將spring-boot-starter-web和spring-boot-starter-security新增到pom.xml來引導 Spring Boot 2.7.5 專案:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.7.5</version>
</dependency>

spring -boot-starter-security依賴項使用 Spring Security 5。

接下來,我們建立一個名為WebSecurityConfig的類:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

在這裡,我們使用@EnableWebSecurity 註釋該類來啟動配置Web請求安全性的過程。此外,我們還啟用方法級授權。接下來,該類擴充套件WebSecurityConfigurerAdapter類以提供各種安全配置方法。

此外,讓我們定義一個記憶體中使用者進行身份驗證:

@Override
void configure(AuthenticationManagerBuilder auth) throws Exception {
    UserDetails user = User.withDefaultPasswordEncoder()
      .username(<font>"Admin")
      .password(
"password")
      .roles(
"ADMIN")
      .build();
    auth.inMemoryAuthentication().withUser(user);
}

在上面的方法中,我們透過覆蓋預設配置來建立記憶體中使用者。

繼續,讓我們透過重寫configure(WebSecurity web)方法從安全性中排除靜態資源:

@Override
void configure(WebSecurity web) {
    web.ignoring().antMatchers(<font>"/js/**", "/css/**");
}

最後,讓我們透過重寫configure(HttpSecurity http)方法來建立HttpSecurity :

@Override
void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers(<font>"/").permitAll()
      .anyRequest().authenticated()
      .and()
      .formLogin()
      .and()
      .httpBasic();
}

值得注意的是,此設定展示了典型的 Spring Security 5 配置。在後續部分中,我們將將此程式碼遷移到 Spring Security 6。

將專案遷移到 Spring Security 6
Spring 建議採用增量遷移方法,以防止更新到 Spring Security 6 時破壞現有程式碼。在升級到 Spring Security 6 之前,我們可以先將 Spring Boot 應用程式升級到 Spring Security 5.8.5 並更新程式碼以使用新功能。遷移到 5.8.5 讓我們為版本 6 中的預期變化做好準備。

在增量遷移時,我們的 IDE 可以警告我們已棄用的功能。這有助於增量更新過程。

為簡單起見,我們將應用程式更新為使用 Spring Boot 版本 3.2.2,將示例專案直接遷移到 Spring Security 6。如果應用程式使用 Spring Boot 版本 2,我們可以在屬性部分指定 Spring Security 6。

要開始遷移過程,讓我們修改 pom.xml以使用最新的 Spring Boot 版本:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.2.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.2.2</version>
</dependency>

在初始設定中,我們使用 Spring Boot 2.7.5,它在底層使用 Spring Security 5。

值得注意的是,Spring Boot 3 的最低 Java 版本是 Java 17。

在後續小節中,我們將重構現有程式碼以使用 Spring Security 6。

配置註解
在 Spring Security 6 之前,@Configuration 註解是@EnableWebSecurity 的一部分,但是在最新的更新中,我們必須使用@Configuration註解來註解我們的安全配置:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}

在這裡,我們將@Configuration註解引入到現有的程式碼庫中,因為它不再是@EnableWebSecurity註解的一部分。此外,該註釋不再是@EnableMethodSecurity、@EnableWebFluxSecurity或@EnableGlobalMethodSecurity註釋的一部分。

此外,@EnableGlobalMethodSecurity已標記為棄用並由@EnableMethodSecurity替換。預設情況下,它啟用 Spring 的 pre-post 註解。因此,我們引入@EnableMethodSecurity來提供方法級別的授權

Web安全配置介面卡
最新更新刪除了WebSecurityConfigurerAdapter類並採用基於元件的配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
}

在這裡,我們刪除了WebSecurityConfigurerAdapter,這消除了安全配置的重寫方法。相反,我們可以註冊一個 bean 來進行安全配置。我們可以註冊WebSecurityCustomizer bean 來配置 Web 安全,註冊SecurityFilterChain bean 來配置 HTTP 安全,註冊InMemoryUserDetails bean 來註冊自定義使用者等。

WebSecurityCustomizer Bean
讓我們透過釋出WebSecurityCustomizer bean來修改排除靜態資源的方法:

@Bean
WebSecurityCustomizer webSecurityCustomizer() {
   return (web) -> web.ignoring().requestMatchers(<font>"/js/**", "/css/**");
}

WebSecurityCustomizer介面替換了WebSecurityConfigurerAdapter介面中的configure(Websecurity web)。

認證管理器Bean
在前面的部分中,我們透過從WebSecurityConfigurerAdapter重寫configure(AuthenticationManagerBuilder auth) 建立了一個記憶體使用者。

讓我們透過註冊InMemoryUserDetailsManager bean來重構身份驗證憑據邏輯:

@Bean
InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.withDefaultPasswordEncoder()
      .username(<font>"Admin")
      .password(
"admin")
      .roles(
"USER")
      .build();
    return new InMemoryUserDetailsManager(user);
}

在這裡,我們定義一個具有 USER 角色的記憶體使用者來提供基於角色的授權。

HTTP 安全配置
在之前的 Spring Security 版本中,我們透過重寫WebSecurityConfigurer類中的 configure 方法來配置HttpSecurity。由於它在最新版本中被刪除,讓我們註冊SecurityFilterChain bean 以進行 HTTP 安全配置:

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
      .authorizeHttpRequests(
          request -> request
            .requestMatchers(<font>"/").permitAll()
            .anyRequest().authenticated()
      )
      .formLogin(Customizer.withDefaults())
      .httpBasic(Customizer.withDefaults());
   return http.build();
}

在上面的程式碼中,我們將authorizeRequest()方法替換為authorizeHttpRequests()。新方法使用AuthorizationManager API,簡化了重用和定製。

此外,它還透過延遲身份驗證查詢來提高效能。僅當請求需要授權時才會進行身份驗證查詢。

當我們沒有自定義規則時,我們使用Customizer.withDefaults() 方法來使用預設配置。

此外,我們使用requestMatchers() 而不是antMatcher()或mvcMatcher()來保護資源。

請求快取
請求快取有助於在需要進行身份驗證時儲存使用者請求,並在使用者成功進行身份驗證後將使用者重定向到請求。在 Spring Security 6 之前,RequestCache會檢查每個傳入請求以檢視是否有任何已儲存的請求要重定向到。這會讀取每個RequestCache上的HttpSession。

然而,在 Spring Security 6 中,請求快取僅檢查請求是否包含特殊引數名稱“ continue ”。這可以提高效能並防止不必要的HttpSession讀取。

使用OpenRewrite
此外,我們可以使用 OpenRewrite 等第三方工具將現有的 Spring Boot 應用程式遷移到 Spring Boot 3。由於 Spring Boot 3 使用 Spring Security 6,因此它還將安全配置遷移到版本 6。

要使用 OpenRewrite,我們可以在pom.xml中新增一個外掛:

<plugin>
    <groupId>org.openrewrite.maven</groupId>
    <artifactId>rewrite-maven-plugin</artifactId>
    <version>5.23.1</version>
    <configuration>
        <activeRecipes>
            <recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0</recipe>
        </activeRecipes>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.openrewrite.recipe</groupId>
            <artifactId>rewrite-spring</artifactId>
            <version>5.5.0</version>
        </dependency>
    </dependencies>
</plugin>

這裡,我們透過recipe屬性指定升級到Spring Boot版本3。OpenRewrite 有很多用於升級 Java 專案的方法可供選擇。

最後,讓我們執行遷移命令:

$ mvn rewrite:run


上面的命令將專案遷移到 Spring Boot 3,包括安全配置。但是,OpenRewrite 目前不使用 lambda DSL 進行遷移的安全配置。當然,這可能會在未來的版本中發生變化。

相關文章