How is Spring Security configured in modern versions (SecurityFilterChain bean) versus the older WebSecurityConfigurerAdapter?

8 minintermediatesecurityfilterchainwebsecurityconfigureradapterconfiguration

Quick Answer

Older Spring Security configuration extended WebSecurityConfigurerAdapter and overrode a configure(HttpSecurity) method — deprecated since Spring Security 5.7 and removed in later versions. Modern configuration is component-based: you declare one or more @Bean methods returning a SecurityFilterChain, built by configuring an injected HttpSecurity object with a fluent, lambda-based DSL — avoiding the rigid single-inheritance constraint of the old adapter class and making it straightforward to define multiple independent filter chains for different parts of an application.

Detailed Answer

Older style (deprecated since Spring Security 5.7, removed in 6.x): security configuration was defined by extending WebSecurityConfigurerAdapter and overriding its configure(HttpSecurity http) method:

@Configuration
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and().formLogin();
    }
}

This relied on class inheritance — a real limitation, since Java's single inheritance meant a configuration class could only extend one adapter, making it awkward to compose or define multiple independent security configurations cleanly.

Modern style (component-based, since Spring Security 5.7+): you declare a SecurityFilterChain as a plain @Bean, built via a fluent, largely lambda-based DSL applied to an injected HttpSecurity instance:

@Configuration
@EnableWebSecurity
class SecurityConfig {
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated())
            .formLogin(Customizer.withDefaults())
            .csrf(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Advantages of the newer approach:

  • No inheritance constraint — configuration is just a @Bean method, composable like any other Spring configuration.
  • Multiple independent SecurityFilterChain beans are straightforward to define, each matched to a different request pattern via securityMatcher(...), letting different parts of an application (e.g., a stateless JWT-secured /api/** versus a session-based form-login admin console) coexist cleanly with entirely separate security rules.
  • More consistent, lambda-DSL style aligned with how the rest of modern Spring configuration (WebMvcConfigurer, etc.) is typically written.

Anyone maintaining an older codebase should expect to see WebSecurityConfigurerAdapter-based configuration, but new code should always use the SecurityFilterChain @Bean approach — the adapter class is fully removed in current Spring Security major versions.