Reasons Spring Boot 3 Changed WebSecurityConfigurerAdapter — and How to Migrate Gracefully
Every developer who has touched Spring Security before 2023 remembers one thing:WebSecurityConfigurerAdapter was the loyal gatekeeper of your web fortress. You’d override a few methods, call http.authorizeRequests(), and voilà — your app was safe...

I am Tuanh.net. As of 2024, I have accumulated 8 years of experience in backend programming. I am delighted to connect and share my knowledge with everyone.
1. Introduction: When the Old Guard Retires
1.1 Why WebSecurityConfigurerAdapter Was Removed
WebSecurityConfigurerAdapter and override configure(HttpSecurity) or configure(AuthenticationManagerBuilder). This inheritance pattern made configuration hard to reason about, especially when multiple adapters or configurations were combined — which led to mysterious behavior and security bugs.
SecurityFilterChaindefines what to protect and how.AuthenticationManagerandUserDetailsServiceare beans in the context.- Everything is wired explicitly, so Spring can easily optimize and inspect it.
1.2 Old vs. New: The Practical Comparison
// Old style (Spring Boot 2.x)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/public/").permitAll()
.antMatchers("/api/admin/").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin")
.password("{noop}123456")
.roles("ADMIN");
}
}
WebSecurityConfigurerAdapter no longer exists.
// New style (Spring Boot 3.x)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/").permitAll()
.requestMatchers("/api/admin/").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService users() {
UserDetails admin = User.withUsername("admin")
.password("{noop}123456")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(admin);
}
}
csrf, authorizeHttpRequests, etc.) is now a lambda-based builder — a step toward the new functional DSL mindset.
1.3 Deep Dive: What’s Really Happening Under the Hood
SecurityFilterChain bean, you are effectively describing how Spring Security should build its chain of filters — like a precise blueprint.
SecurityConfig now declares it.This aligns with the new Spring Bean lifecycle rules in Boot 3, where every major configuration (web, data, security) prefers bean-based declarations.
HttpSecurityis no longer configured implicitly.UserDetailsServiceis injected directly into the context.- Password encoding, CSRF handling, and exception translation are now modular components you can inject or override at will.
@AutoConfigureMockMvc
@SpringBootTest
class SecurityTests {
@Autowired
private MockMvc mockMvc;
@Test
void shouldAllowPublicEndpoint() throws Exception {
mockMvc.perform(get("/api/public/hello"))
.andExpect(status().isOk());
}
}
@WithMockUser issues that used to arise from improperly configured WebSecurityConfigurerAdapter.
1.4 Migration Pitfalls You’ll Likely Encounter
Compile-time errors:Your
extends WebSecurityConfigurerAdapteris gone — and allconfigure()overrides fail.Solution: Replace them with@Bean SecurityFilterChain.Broken antMatchers():
antMatchers()is replaced byrequestMatchers().It now supports both servlet and reactive environments through a unified interface.AuthenticationManager confusion:Since
AuthenticationManageris no longer auto-created, you might get“No AuthenticationManager defined” errors.To fix this, define it explicitly:@Beanpublic AuthenticationManager authManager(UserDetailsService userDetailsService) { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userDetailsService); provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); return new ProviderManager(provider);}
2. Beyond Migration: Embracing the New Paradigm
WebSecurityConfigurerAdapter is more than a refactor; it’s a paradigm shift.Spring Security now behaves like modern Java — explicit, modular, and optimized for native and reactive use cases.
2.1 Why This Matters for Real Applications
@Bean
@Order(1)
SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt());
return http.build();
}
@Bean
@Order(2)
SecurityFilterChain webChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.formLogin(Customizer.withDefaults());
return http.build();
}
2.2 Security in the Age of GraalVM and Native Images
WebSecurityConfigurerAdapter doesn’t play well with AOT (ahead-of-time) compilation. The new bean-based API is AOT-friendly, enabling native builds without obscure configuration hints.
3. Conclusion
WebSecurityConfigurerAdapter wasn’t a bug — it was a signal.Spring Security is stepping into a declarative, testable, cloud-native era.Yes, migration might sting a little, but it unlocks better control, clearer configuration, and performance gains for modern Java environments.
SecurityFilterChain and functional builders, you don’t just fix your app — you future-proof it.
AuthenticationProvider that broke after upgrading to Spring Boot 3? Share your experience or question below — let’s untangle it together.
Read more at : Reasons Spring Boot 3 Changed WebSecurityConfigurerAdapter — and How to Migrate Gracefully





