The Component Revolution: Mastering Spring Security 6.x Bean Migration
Writing
SECURITY
February 2, 20264 min read

The Component Revolution: Mastering Spring Security 6.x Bean Migration

The definitive guide to migrating from WebSecurityConfigurerAdapter to the SecurityFilterChain bean model. Understand the "why", master the "how", and future-proof your security configuration.

Spring BootJavaSecurityMigrationArchitecture

The landscape of Java security has completely shifted. If you've been working with Spring Boot for more than a few years, your muscle memory probably starts every security config with extends WebSecurityConfigurerAdapter.

I know mine did. For years, that class was my safety blanket.

But in Spring Security 5.7, it was deprecated. And in Spring Security 6? It's gone.

Now, before you panic (or get annoyed at yet another breaking change), let me tell you: This is actually a good thing. It’s not just a rename; it’s a total paradigm shift from Inheritance to Composition.

In this guide, we’re going to dismantle the old ways and rebuild a modern, component-based security architecture together.

Why did Spring Security move from inheritance to composition?

Let's be real—the legacy configuration was a bit of a monolith. You had one class that controlled everything.

// LEGACY: The Monolith
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ... mixed concerns ...
    }
}

The problem? Well, what if you wanted distinct security rules for your /api/v1/* endpoints (Stateless, JWT) and your /admin/* dashboard (Stateful, Session)? You had to create multiple static classes, deal with complex ordering, and fight the framework. It was a mess.

How do you configure SecurityFilterChain in Spring Security 6.x?

In this new "Component Revolution," HttpSecurity is just a builder that produces a SecurityFilterChain. You register this chain as a bean. The IoC container handles the rest.

It feels much more like "Spring" than the old way ever did.

Side-by-Side Comparison

Let's look at a standard configuration. We want to:

  1. Disable CSRF (because who needs it for APIs, right?).
  2. Allow public access to /auth/*.
  3. Lock down everything else.
  4. Go stateless with our sessions.
// LEGACY (Spring Boot 2.x)
@Configuration
public class LegacySecurityConfig 
  extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) 
      throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(
                    SessionCreationPolicy.STATELESS
                )
            .and()
            .authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();
    }
}
// MODERN (Spring Boot 3.x / Security 6)
@Configuration
@EnableWebSecurity
public class ModernSecurityConfig {
 
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) 
      throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(
                    SessionCreationPolicy.STATELESS
                )
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
            );
            
        return http.build();
    }
}
Spring Security Filter Chain Diagram

Notice the shift to DSL Lambdas (csrf -> csrf.disable()). This makes the nesting and configuration scope explicit, improving readability and reducing indentation hell.

How do you use multiple SecurityFilterChains for different endpoints?

The true power comes when you need to mix strategies.

Imagine you have a specialized chain just for your internal actuator endpoints. You want this to strictly check for a specific Role and have a higher precedence.

@Bean
@Order(1) // Runs BEFORE the main chain
public SecurityFilterChain actuatorSecurity(HttpSecurity http) throws Exception {
    http
        .securityMatcher("/actuator/**") // Only matches these URLs
        .authorizeHttpRequests(auth -> auth
            .anyRequest().hasRole("ADMIN")
        )
        .httpBasic(withDefaults()); // Simple auth for monitoring tools
        
    return http.build();
}
 
@Bean
@Order(2) // Fallback chain
public SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            // ... standard app rules ...
        )
        // ... JWT filters ...
        
    return http.build();
}

Custom Request Matchers

In the legacy version, creating custom matching logic was verbose. Now, RequestMatcher is a functional interface.

@Bean
public SecurityFilterChain customChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers(request -> 
                request.getHeader("X-API-KEY") != null
            ).permitAll()
            .anyRequest().authenticated()
        );
    return http.build();
}

Conclusion

The "Component Revolution" in Spring Security forces us to stop thinking about "Configuring the Framework" and start thinking about "Defining Security Beans".

It aligns Spring Security with the rest of the Spring ecosystem: explicit, composable, and easier to test.

Upgrading to Spring Boot 3? Don't just Ctrl+C, Ctrl+V your old config. Embrace the chain.

For the official migration guide, see the Spring Security 6.x Migration Documentation and the Spring Security Architecture Reference.

Keep Reading

Frequently Asked Questions

Why was WebSecurityConfigurerAdapter deprecated?

It encouraged a rigid, inheritance-based configuration that made it difficult to compose multiple security policies. The new component-based model allows for more flexible, independent SecurityFilterChain beans.

Can I have multiple SecurityFilterChain beans?

Yes! This is the power of the new model. You can define multiple beans with different `@Order` annotations to handle API routes, Admin panels, and public UI logic separately.

How do I expose the AuthenticationManager now?

Instead of overriding `authenticationManagerBean()`, you simply inject `AuthenticationConfiguration` and call `configuration.getAuthenticationManager()`.

Last updated: April 2, 2026

Rabinarayan Patra

Rabinarayan Patra

SDE II at Amazon. Previously at ThoughtClan Technologies building systems that processed 700M+ daily transactions. I write about Java, Spring Boot, microservices, and the things I figure out along the way. More about me →

X (Twitter)LinkedIn

Stay in the loop

Get the latest articles on system design, frontend & backend development, and emerging tech trends — straight to your inbox. No spam.