如何从Spring Security 5迁移到Spring Security 6/Spring Boot 3

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("Admin")
      .password(
"password")
      .roles(
"ADMIN")
      .build();
    auth.inMemoryAuthentication().withUser(user);
}

在上面的方法中,我们通过覆盖默认配置来创建内存中用户。

继续,让我们通过重写configure(WebSecurity web)方法从安全性中排除静态资源:

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

最后,让我们通过重写configure(HttpSecurity http)方法来创建HttpSecurity :

@Override
void configure(HttpSecurity http) throws Exception {
    http
      .authorizeRequests()
      .antMatchers("/").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("/js/**", "/css/**");
}

WebSecurityCustomizer接口替换了WebSecurityConfigurerAdapter接口中的configure(Websecurity web)。

认证管理器Bean
在前面的部分中,我们通过从WebSecurityConfigurerAdapter重写configure(AuthenticationManagerBuilder auth) 创建了一个内存用户。

让我们通过注册InMemoryUserDetailsManager bean来重构身份验证凭据逻辑:

@Bean
InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.withDefaultPasswordEncoder()
      .username("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("/").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 进行迁移的安全配置。当然,这可能会在未来的版本中发生变化。