Migrating an Application from Spring Security 5 to Spring Security 6
Spring Security offers extensive security features for Java applications. With each new release, Spring Security brings enhancements, security fixes, and upgraded functionalities. Transitioning our application from Spring Security 5 to Spring Security 6 enables us to utilize the most recent advancements and uphold a secure application environment. This article aims to guide you through the migration process, highlighting some key changes and providing code examples.
1. Why Migrate to Spring Security 6/Spring Boot 3?
Spring Security 6 and Spring Boot 3 introduce various improvements, bug fixes, and updated dependencies that enhance the security and performance of your application. Migrating to these versions ensures you benefit from:
- Security Enhancements: Spring Security 6 introduces enhanced security features and updates to protect your application against evolving security threats. This includes improvements in authentication mechanisms, authorization controls, and secure defaults.
- Compatibility with Latest Spring Boot Versions: Spring Security 6 is designed to work seamlessly with the latest versions of Spring Boot (such as Spring Boot 3), providing better integration and compatibility with other Spring ecosystem components.
- Support for New Features and APIs: By migrating to Spring Security 6, you gain access to new features, APIs, and capabilities introduced in the latest release. This allows you to leverage modern security practices and patterns in your application development.
- Performance Improvements: Spring Security 6 may include performance optimizations and efficiency enhancements, resulting in better response times and reduced resource utilization. This is crucial for high-traffic applications.
- Stay Up-to-Date with Community Standards: Staying current with Spring Security versions ensures that your application adheres to community standards and best practices recommended by the Spring framework maintainers and security experts.
2. Preparing for Migration
Before starting the migration process, ensure you are on the latest version of Spring Security 5.8.x. This version prepares your codebase for the transition. You can upgrade Spring Boot to version 2.7.x if applicable and might need to manually override dependency versions:
<properties> <spring-security.version>5.8.12</spring-security.version> </properties>
2.1 Key Changes in Spring Security 6
- Configuration with WebFilterChain: Spring Security 6 removes the
WebSecurityConfigurerAdapter
class. Instead, we will define aSecurityFilterChain
bean. - Lambda Expressions: Spring Security 6 embraces lambda expressions for a cleaner and more concise configuration style.
- Authentication Providers: The approach to authentication providers has shifted slightly.
2.1 Upgrade Dependencies
To update the project’s dependencies to include Spring Security 6.x.x and Spring Boot 3.x.x, upgrade the parent version spring-boot-starter-parent
of the Spring Boot project from 2.7.15 to 3.x.x and it will use the Spring Security 6.x.x compatible version:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <relativePath/> <!-- lookup parent from repository --> </parent>
For example, updating the above project’s pom.xml
spring-boot-starter-parent
version from 2.7.15 to 3.2.5 updates the project to use spring-boot-starter-security
3.2.5 which uses Spring Security 6.2.4.
Confirm that the pom.xml
file includes the dependencies for spring-boot-starter-web
and spring-boot-starter-security
compatible with Spring Security 6 and Spring Boot 3:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Note that the Java version requirement for Spring Boot 3 is a minimum of Java 17.
2.2 Security Configuration Evolution
Review your Spring Security configuration classes (SecurityConfig.java
or similar) for any changes required in configuration patterns or security rules introduced in Spring Security 6. Below are examples demonstrating key changes when migrating from Spring Security 5 to Spring Security 6.
2.2.1 Authentication and Configuration Changes
Spring Security 5 Configuration (SecurityConfig.java)
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final String ADMIN = "ADMIN"; private static final String USER = "USER"; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/api/employees/**").hasRole(USER) .antMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated() .and() .httpBasic() .and() .formLogin() .and() .logout() .and() .csrf() .disable(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { UserDetails user = User.withDefaultPasswordEncoder() .username("jcg") .password("javacodegeeks") .roles(ADMIN) .username("william") .password("lee") .roles(USER) .build(); auth.inMemoryAuthentication().withUser(user); } }
Spring Security 6 Configuration (SecurityConfig.java)
@Configuration @EnableWebSecurity public class SecurityConfig { private static final String ADMIN = "ADMIN"; private static final String USER = "USER"; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(request -> request .requestMatchers("/api/employees/**").hasRole(USER) .requestMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated()) .httpBasic(httpSecurityHttpBasicConfigurer -> httpSecurityHttpBasicConfigurer.disable()) .formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.disable()) .logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer.disable()) .csrf(AbstractHttpConfigurer::disable); return http.build(); } @Bean public UserDetailsService userDetailsService() throws Exception { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User .withUsername("thomas") .password(encoder().encode("paine")) .roles(ADMIN).build()); manager.createUser(User .withUsername("bill") .password(encoder().encode("withers")) .roles(USER).build()); return manager; } @Bean public PasswordEncoder encoder() { return new BCryptPasswordEncoder(); } }
3. Differences: Spring Security 5 vs. Spring Security 6
Below are some differences between the two Spring Security configurations:
3.1 Configuration Style
In Spring Security 5, a typical security configuration class looks like:
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter {
In Spring Security 6, it is now:
@Configuration @EnableWebSecurity public class SecurityConfig {
In the first configuration with Spring Security 5, SecurityConfig
extends WebSecurityConfigurerAdapter
. This was the traditional approach to configuring Spring Security, whereby a class extends the specific base class of WebSecurityConfigurerAdapter
.
In the second configuration with Spring Security 6, SecurityConfig
is annotated with @Configuration
and @EnableWebSecurity
, indicating that it’s a standalone configuration class for Spring Security.
3.2 Configuring HTTP Security
In Spring Security 5, the configure(HttpSecurity http)
method is overridden within a class that extends WebSecurityConfigurerAdapter
. This method allows us to configure HTTP security settings, such as URL-based authorization rules, form login, HTTP basic authentication, logout, CSRF protection, and more. The configuration is imperative and directly modifies the provided HttpSecurity
object.
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final String ADMIN = "ADMIN"; private static final String USER = "USER"; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/api/employees/**").hasRole(USER) .antMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated() .and() .httpBasic() .and() .formLogin() .and() .logout() .and() .csrf() .disable(); } }
In Spring Security 6, the approach shifts towards functional configuration. Instead of overriding a method within WebSecurityConfigurerAdapter
, we define a SecurityFilterChain
bean using a method annotated with @Bean
. This bean defines the security filter chain, including HTTP security configurations, using method chaining or lambda expressions. The HttpSecurity
object is passed as a parameter to this method, allowing for configuration within the bean definition.
@Configuration @EnableWebSecurity public class SecurityConfig { private static final String ADMIN = "ADMIN"; private static final String USER = "USER"; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(request -> request .requestMatchers("/api/employees/**").hasRole(USER) .requestMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated()) .httpBasic(httpSecurityHttpBasicConfigurer -> httpSecurityHttpBasicConfigurer.disable()) .formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.disable()) .logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer.disable()) .csrf(AbstractHttpConfigurer::disable); return http.build(); } }
3.3 Method Configuration vs. Functional Configuration
First Configuration (Spring Security 5)
@Override protected void configure(HttpSecurity http) throws Exception {
Second Configuration: (Spring Security 6)
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- In the first configuration, method configuration is used, where you override the
configure(HttpSecurity http)
method to define security configurations. - In the second configuration, functional configuration is used with Java Config DSL, where you define security configurations using method chaining within a
SecurityFilterChain
bean definition.
3.4 Authentication Configuration
In Spring Security 5, you override the configure(AuthenticationManagerBuilder auth)
method that extends the WebSecurityConfigurerAdapter
class to define in-memory user details for authentication.
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { UserDetails user = User.withDefaultPasswordEncoder() .username("jcg") .password("javacodegeeks") .roles(ADMIN) .username("william") .password("lee") .roles(USER) .build(); auth.inMemoryAuthentication().withUser(user); }
With Spring Security 6, we define a method userDetailsService()
that returns a UserDetailsService
bean to create an InMemoryUserDetailsManager
instance to manage user details in memory. This approach is more modular and flexible, allowing for easier management of user details and customization.
@Bean public UserDetailsService userDetailsService() throws Exception { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User .withUsername("thomas") .password(encoder().encode("paine")) .roles(ADMIN).build()); manager.createUser(User .withUsername("bill") .password(encoder().encode("withers")) .roles(USER).build()); return manager; }
3.5 Lambda expressions
In Spring Security 5, we use the .and()
definitions between configuration elements and utilized the .antMatchers
method of the authorizeRequest
object.
http .authorizeRequests() .antMatchers("/api/employees/**").hasRole(USER) .antMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated() .and() .httpBasic() .and() .formLogin() .and() .logout() .and() .csrf() .disable();
In Spring Security 6, the .and()
definitions used between configuration elements in the older configuration (Spring Security 5) have been removed. They have been substituted with lambda expressions in Spring Security 6.2.4. Also, the .antMatchers
method has been renamed to .requestMatchers
and we replace authorizeRequests()
method with authorizeHttpRequests().
http .authorizeHttpRequests(request -> request .requestMatchers("/api/employees/**").hasRole(USER) .requestMatchers("/api/employee/**").hasRole(ADMIN) .anyRequest().authenticated()) .httpBasic(httpSecurityHttpBasicConfigurer -> httpSecurityHttpBasicConfigurer.disable()) .formLogin(httpSecurityFormLoginConfigurer -> httpSecurityFormLoginConfigurer.disable()) .logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer.disable()) .csrf(AbstractHttpConfigurer::disable);
4. Use OpenRewrite for Spring Security 6 Migration
OpenRewrite can automate some of the migration tasks for us. OpenRewrite is a tool that helps automate source code migrations, including upgrading dependencies like Spring Security. Here is how we can utilize OpenRewrite to streamline the migration process from Spring Security 5.0 to 6.0.
4.1 Configuring OpenRewrite in pom.xml
Add the OpenRewrite Maven plugin to the project’s pom.xml
file. Below is an example configuration:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.openrewrite.maven</groupId> <artifactId>rewrite-maven-plugin</artifactId> <version>5.29.0</version> <configuration> <activeRecipes> <recipe>org.openrewrite.java.migrate.UpgradeToJava17</recipe> <recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2</recipe> </activeRecipes> </configuration> <dependencies> <dependency> <groupId>org.openrewrite.recipe</groupId> <artifactId>rewrite-migrate-java</artifactId> <version>2.7.1</version> </dependency> <dependency> <groupId>org.openrewrite.recipe</groupId> <artifactId>rewrite-spring</artifactId> <version>5.8.0</version> </dependency> </dependencies> </plugin> </plugins> </build>
4.2 Apply Rewrite to Project
Execute the mvn rewrite:run
in your terminal to run the migration command. Fig 1 shows the output result of Maven:
The full output looks like this:
[WARNING] Changes have been made to Downloads/employee-service-2/pom.xml by: [WARNING] org.openrewrite.java.migrate.UpgradeToJava17 [WARNING] org.openrewrite.java.migrate.JavaVersion17 [WARNING] org.openrewrite.java.migrate.UpgradeJavaVersion: {version=17} [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2 [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_1 [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0 [WARNING] org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7 [WARNING] org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=2.7.x} [WARNING] org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=3.0.x} [WARNING] org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=3.1.x} [WARNING] org.openrewrite.maven.UpgradeParentVersion: {groupId=org.springframework.boot, artifactId=spring-boot-starter-parent, newVersion=3.2.x} [WARNING] Changes have been made to Downloads/employee-service-2/src/main/java/com/javacodegeeks/example/employeeservice/SecurityConfig.java by: [WARNING] org.openrewrite.java.migrate.UpgradeToJava17 [WARNING] org.openrewrite.java.migrate.JavaVersion17 [WARNING] org.openrewrite.java.migrate.UpgradeJavaVersion: {version=17} [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2 [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_1 [WARNING] org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0 [WARNING] org.openrewrite.java.spring.boot3.ConfigurationOverEnableSecurity: {forceAddConfiguration=false} [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0 [WARNING] org.openrewrite.java.spring.security5.UpgradeSpringSecurity_5_8 [WARNING] org.openrewrite.java.spring.security5.UpgradeSpringSecurity_5_7 [WARNING] org.openrewrite.java.spring.security5.WebSecurityConfigurerAdapter [WARNING] org.openrewrite.java.spring.security5.AuthorizeHttpRequests [WARNING] org.openrewrite.java.spring.security5.UseNewRequestMatchers [WARNING] org.openrewrite.java.spring.security5.ReplaceGlobalMethodSecurityWithMethodSecurity [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_1 [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0 [WARNING] org.openrewrite.java.spring.security5.UpgradeSpringSecurity_5_8 [WARNING] org.openrewrite.java.spring.security5.UseNewRequestMatchers [WARNING] org.openrewrite.java.spring.boot2.HttpSecurityLambdaDsl [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_2 [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_1 [WARNING] org.openrewrite.java.spring.security6.UpgradeSpringSecurity_6_0 [WARNING] org.openrewrite.java.spring.security5.UpgradeSpringSecurity_5_8 [WARNING] org.openrewrite.java.spring.security5.UseNewRequestMatchers [WARNING] org.openrewrite.java.spring.boot2.HttpSecurityLambdaDsl [WARNING] Please review and commit the results. [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
Next, analyze and observe the difference and update your codebase based on the configured recipes.
5. Conclusion
In this article, we explored the process of migrating from Spring Security 5.0 to 6.0, emphasizing the importance of staying updated with the latest security enhancements and features. We discussed the manual migration steps, such as updating dependencies, configuring security settings, and handling deprecated APIs. Additionally, we highlighted the use of OpenRewrite as a tool for automating code migrations, ensuring a smoother transition to the newer version of Spring Security.
Both configurations achieve the same goal of configuring Spring Security for the application but they use different approaches and APIs. The first configuration follows the traditional method-based approach, while the second configuration utilizes a newer functional configuration.
6. Download the Source Code
This was an article on how to migrate from Spring Security 5 to 6.
You can download the full source code of this example here: Spring Security: Migrate from 5 to 6