Spring Security 6 Migration Skill
Overview
Spring Security 6 (included in Spring Boot 3) removes the deprecated WebSecurityConfigurerAdapter and introduces a component-based configuration approach using SecurityFilterChain beans.
Key Changes
1. Remove WebSecurityConfigurerAdapter
The biggest change is moving from class extension to bean configuration.
Before (Spring Security 5 / Spring Boot 2)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
After (Spring Security 6 / Spring Boot 3)
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
2. Method Security Annotation Change (CRITICAL)
This is a required change. The @EnableGlobalMethodSecurity annotation is removed in Spring Security 6 and must be replaced with @EnableMethodSecurity.
// BEFORE (Spring Security 5 / Spring Boot 2) - WILL NOT COMPILE in Spring Boot 3 @EnableGlobalMethodSecurity(prePostEnabled = true) // AFTER (Spring Security 6 / Spring Boot 3) - REQUIRED @EnableMethodSecurity(prePostEnabled = true)
Import Change
// BEFORE import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; // AFTER import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
Quick Migration Command
# Replace the annotation in all Java files
find . -name "*.java" -type f -exec sed -i 's/@EnableGlobalMethodSecurity/@EnableMethodSecurity/g' {} +
# Also update the import statement
find . -name "*.java" -type f -exec sed -i 's/EnableGlobalMethodSecurity/EnableMethodSecurity/g' {} +
Verify @EnableMethodSecurity Is Present
After migration, confirm the new annotation exists:
# This should return results showing your security config class grep -r "@EnableMethodSecurity" --include="*.java" .
If this returns no results but you're using method-level security (@PreAuthorize, @PostAuthorize, etc.), the migration is incomplete.
3. Lambda DSL Configuration
Spring Security 6 uses lambda-based configuration:
// Before (chained methods)
http
.csrf().disable()
.cors().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated();
// After (lambda DSL)
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
);
4. URL Matching Changes
antMatchers() is replaced with requestMatchers():
// Before
.antMatchers("/api/**").authenticated()
.antMatchers(HttpMethod.POST, "/api/users").permitAll()
// After
.requestMatchers("/api/**").authenticated()
.requestMatchers(HttpMethod.POST, "/api/users").permitAll()
5. Exception Handling
// Before
.exceptionHandling()
.authenticationEntryPoint((request, response, ex) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
})
.and()
// After
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
})
)
6. Headers Configuration
// Before
.headers().frameOptions().disable()
// After
.headers(headers -> headers
.frameOptions(frame -> frame.disable())
)
7. UserDetailsService Configuration
// The UserDetailsService bean is auto-detected
// No need to explicitly configure in AuthenticationManagerBuilder
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) {
// Implementation
}
}
Complete Migration Example
Before (Spring Boot 2.x)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint((request, response, ex) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, ex.getMessage());
})
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/users").permitAll()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
}
}
After (Spring Boot 3.x)
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
authException.getMessage());
})
)
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.POST, "/api/users").permitAll()
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/h2-console/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.headers(headers -> headers
.frameOptions(frame -> frame.disable())
);
return http.build();
}
}
Servlet Namespace Change
Don't forget the servlet import change:
// Before import javax.servlet.http.HttpServletResponse; // After import jakarta.servlet.http.HttpServletResponse;
Testing Security
Update security test annotations if needed:
@SpringBootTest
@AutoConfigureMockMvc
class SecurityTests {
@Test
@WithMockUser(roles = "ADMIN")
void adminEndpoint_withAdminUser_shouldSucceed() {
// Test implementation
}
}
Migration Commands Summary
Step 1: Remove WebSecurityConfigurerAdapter
# Find classes extending WebSecurityConfigurerAdapter grep -r "extends WebSecurityConfigurerAdapter" --include="*.java" . # The class must be refactored - cannot be automated with sed
Step 2: Replace Method Security Annotation
# Replace @EnableGlobalMethodSecurity with @EnableMethodSecurity
find . -name "*.java" -type f -exec sed -i 's/@EnableGlobalMethodSecurity/@EnableMethodSecurity/g' {} +
# Update import
find . -name "*.java" -type f -exec sed -i 's/import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity/import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity/g' {} +
Step 3: Replace antMatchers with requestMatchers
# Replace antMatchers
find . -name "*.java" -type f -exec sed -i 's/\.antMatchers(/.requestMatchers(/g' {} +
# Replace mvcMatchers
find . -name "*.java" -type f -exec sed -i 's/\.mvcMatchers(/.requestMatchers(/g' {} +
# Replace regexMatchers
find . -name "*.java" -type f -exec sed -i 's/\.regexMatchers(/.requestMatchers(/g' {} +
Step 4: Replace authorizeRequests with authorizeHttpRequests
find . -name "*.java" -type f -exec sed -i 's/\.authorizeRequests(/.authorizeHttpRequests(/g' {} +
Verification Commands
Verify No Deprecated Patterns Remain
# Should return NO results
grep -r "WebSecurityConfigurerAdapter" --include="*.java" .
grep -r "@EnableGlobalMethodSecurity" --include="*.java" .
grep -r "\.antMatchers(" --include="*.java" .
grep -r "\.authorizeRequests(" --include="*.java" .
Verify New Patterns Are Present
# Should return results
grep -r "@EnableMethodSecurity" --include="*.java" .
grep -r "SecurityFilterChain" --include="*.java" .
grep -r "\.requestMatchers(" --include="*.java" .
grep -r "\.authorizeHttpRequests(" --include="*.java" .
Common Migration Pitfalls
- •
@Configuration is now required separately - Before Spring Security 6,
@Configurationwas part of@EnableWebSecurity. Now you must add it explicitly. - •
Lambda DSL is mandatory - The old chained method style (
http.csrf().disable().and()...) is deprecated and must be converted to lambda style. - •
AuthenticationManager injection changed - Use
AuthenticationConfiguration.getAuthenticationManager()instead of overridingauthenticationManagerBean(). - •
UserDetailsService auto-detection - Spring Security 6 automatically detects
UserDetailsServicebeans; no need for explicit configuration. - •
Method security default changes -
@EnableMethodSecurityenables@PreAuthorizeand@PostAuthorizeby default (unlike the old annotation).