home / skills / benchflow-ai / skillsbench / spring-security-6

This skill guides migrating Spring Security 5 to 6 by converting configurations to SecurityFilterChain, enabling method security, and adopting lambda DSL for

npx playbooks add skill benchflow-ai/skillsbench --skill spring-security-6

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
12.7 KB
---
name: spring-security-6
description: Migrate Spring Security 5 to Spring Security 6 configuration. Use when removing WebSecurityConfigurerAdapter, replacing @EnableGlobalMethodSecurity with @EnableMethodSecurity, converting antMatchers to requestMatchers, or updating to lambda DSL configuration style. Covers SecurityFilterChain beans and authentication manager changes.
---

# 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)

```java
@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)

```java
@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`.

```java
// 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

```java
// BEFORE
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;

// AFTER
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
```

#### Quick Migration Command

```bash
# 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:

```bash
# 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:

```java
// 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()`:

```java
// Before
.antMatchers("/api/**").authenticated()
.antMatchers(HttpMethod.POST, "/api/users").permitAll()

// After
.requestMatchers("/api/**").authenticated()
.requestMatchers(HttpMethod.POST, "/api/users").permitAll()
```

### 5. Exception Handling

```java
// 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

```java
// Before
.headers().frameOptions().disable()

// After
.headers(headers -> headers
    .frameOptions(frame -> frame.disable())
)
```

### 7. UserDetailsService Configuration

```java
// 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)

```java
@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)

```java
@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:

```java
// Before
import javax.servlet.http.HttpServletResponse;

// After
import jakarta.servlet.http.HttpServletResponse;
```

## Testing Security

Update security test annotations if needed:

```java
@SpringBootTest
@AutoConfigureMockMvc
class SecurityTests {

    @Test
    @WithMockUser(roles = "ADMIN")
    void adminEndpoint_withAdminUser_shouldSucceed() {
        // Test implementation
    }
}
```

## Migration Commands Summary

### Step 1: Remove WebSecurityConfigurerAdapter

```bash
# 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

```bash
# 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

```bash
# 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

```bash
find . -name "*.java" -type f -exec sed -i 's/\.authorizeRequests(/.authorizeHttpRequests(/g' {} +
```

## Verification Commands

### Verify No Deprecated Patterns Remain

```bash
# 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

```bash
# 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

1. **@Configuration is now required separately** - Before Spring Security 6, `@Configuration` was part of `@EnableWebSecurity`. Now you must add it explicitly.

2. **Lambda DSL is mandatory** - The old chained method style (`http.csrf().disable().and()...`) is deprecated and must be converted to lambda style.

3. **AuthenticationManager injection changed** - Use `AuthenticationConfiguration.getAuthenticationManager()` instead of overriding `authenticationManagerBean()`.

4. **UserDetailsService auto-detection** - Spring Security 6 automatically detects `UserDetailsService` beans; no need for explicit configuration.

5. **Method security default changes** - `@EnableMethodSecurity` enables `@PreAuthorize` and `@PostAuthorize` by default (unlike the old annotation).

## Sources

- [Baeldung - Migrate from Spring Security 5 to 6](https://www.baeldung.com/spring-security-migrate-5-to-6)
- [Baeldung - Upgrading Deprecated WebSecurityConfigurerAdapter](https://www.baeldung.com/spring-deprecated-websecurityconfigureradapter)
- [Spring Security 6 Reference](https://docs.spring.io/spring-security/reference/)

Overview

This skill migrates Spring Security 5 configurations to Spring Security 6 (Spring Boot 3) with focused, actionable changes. It guides removal of WebSecurityConfigurerAdapter, updates method-security annotations, converts URL matchers, and shows the new lambda DSL and SecurityFilterChain patterns. The content is practical and targeted at developers updating existing Spring Security apps.

How this skill works

The skill inspects typical security configuration patterns and provides concrete replacement recipes: convert classes that extend WebSecurityConfigurerAdapter into SecurityFilterChain beans, replace @EnableGlobalMethodSecurity with @EnableMethodSecurity, and migrate chained DSL calls (antMatchers, authorizeRequests) to the lambda-style requestMatchers and authorizeHttpRequests. It also covers AuthenticationManager, UserDetailsService detection, servlet import changes, and common exception/header configurations.

When to use it

  • Upgrading a project from Spring Boot 2 / Spring Security 5 to Spring Boot 3 / Spring Security 6
  • Removing WebSecurityConfigurerAdapter and refactoring security configuration classes
  • Switching from chained DSL to the lambda-based HttpSecurity configuration style
  • Replacing antMatchers/mvcMatchers/regexMatchers with requestMatchers
  • Ensuring method-level security annotations compile after the upgrade

Best practices

  • Add @Configuration explicitly on security config classes and define SecurityFilterChain beans rather than extending WebSecurityConfigurerAdapter
  • Replace @EnableGlobalMethodSecurity with @EnableMethodSecurity and update imports before compiling
  • Prefer AuthenticationConfiguration.getAuthenticationManager() for AuthenticationManager injection
  • Use requestMatchers(...) and authorizeHttpRequests(auth -> ...) consistently with lambda DSL
  • Run grep/sed checks to verify no deprecated patterns remain and that new patterns exist

Example use cases

  • Refactor a SecurityConfig class that extended WebSecurityConfigurerAdapter into a component-based SecurityFilterChain implementation
  • Automate annotation replacement from @EnableGlobalMethodSecurity to @EnableMethodSecurity across the codebase
  • Convert all .antMatchers/.mvcMatchers/.regexMatchers to .requestMatchers and update authorizeRequests to authorizeHttpRequests
  • Adjust exceptionHandling, headers, and sessionManagement clauses to the lambda DSL form
  • Update servlet imports from javax.servlet.http.HttpServletResponse to jakarta.servlet.http.HttpServletResponse

FAQ

Will replacing @EnableGlobalMethodSecurity with @EnableMethodSecurity break method-level security?

No — @EnableMethodSecurity is the required replacement in Spring Security 6 and re-enables @PreAuthorize/@PostAuthorize; update imports and confirm the annotation is present.

Can I automate the entire migration with sed/grep?

You can automate text replacements for annotations and matcher names, but refactoring classes that extend WebSecurityConfigurerAdapter to SecurityFilterChain beans requires manual code changes and testing.