Spring Security Request Matchers in Spring Boot 3
Problem Statement
When migrating a Spring Boot application from version 2.7 to 3.0, developers encounter breaking changes in Spring Security configuration. The core issue involves securing REST applications while allowing public access to specific endpoints (like OpenAPI documentation). In Spring Boot 2.7, using WebSecurityConfigurerAdapter
with antMatchers()
worked perfectly to exempt paths from authentication:
// Spring Boot 2.7 approach (deprecated in 3.0)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated();
}
After upgrading to Spring Boot 3.0, where WebSecurityConfigurerAdapter
was removed, many developers try this modern approach:
// Common problematic approach in 3.0
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests
.requestMatchers("/openapi/openapi.yml").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
The issue: Despite this configuration, Spring Boot 3 still blocks access to the OpenAPI endpoint, requiring authentication for what should be a public resource.
Solution: Using Explicit Request Matchers
Spring Security 6 introduced stricter path matching that requires explicit RequestMatcher
declarations instead of plain strings.
Optimal Configuration
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(requests -> requests
.requestMatchers(
new AntPathRequestMatcher("/openapi/openapi.yml")
).permitAll()
.requestMatchers(
new AntPathRequestMatcher(HttpMethod.OPTIONS, "/**")
).permitAll() // Enable CORS preflight
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
Explanation of Key Changes
Explicit AntPathRequestMatcher
Wrapping paths innew AntPathRequestMatcher()
provides explicit path-matching behavior required by Spring Security 6CORS Preflight Handling
The OPTIONS request matcher enables Swagger UI and other API clients to work properlyModern CSRF Configuration
Usingcsrf.disable()
in the lambda DSL ensures proper stateless API behavior
Full Whitelist for Swagger/OpenAPI
For applications exposing API documentation, add these common endpoints:
private static final String[] AUTH_WHITELIST = {
"/v2/api-docs",
"/v3/api-docs/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/swagger-ui.html"
};
.requestMatchers(
Arrays.stream(AUTH_WHITELIST)
.map(AntPathRequestMatcher::new)
.toArray(AntPathRequestMatcher[]::new)
).permitAll()
Alternative Approach: Multiple Security Chains
For complex security requirements, create separate security filter chains with specific path matching:
@Configuration
@EnableWebSecurity
public class MultiSecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain openApiFilter(HttpSecurity http) throws Exception {
http.securityMatcher("/openapi/openapi.yml", "/swagger-ui/**")
.authorizeHttpRequests(requests ->
requests.anyRequest().permitAll()
);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain mainFilter(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(requests ->
requests.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
Key Features
@Order
controls execution prioritysecurityMatcher()
defines paths for specific chains- Clear separation of public and private routes
Important Considerations
Version Compatibility
- Use Spring Boot 3.0 GA or later (some request matcher issues were fixed after 3.0 RC2)
- Keep
spring-boot-starter-parent
updated to latest 3.x version
Deprecated Methods
Avoid these legacy patterns:
antMatchers()
- removed in Spring Security 6mvcMatchers()
- not recommendedregexMatchers()
- useRegexRequestMatcher
instead
Best Practices
- Use constants for path patterns
- Combine with method security
- Disable CSRF for stateless APIs
- Prefer lambda DSL configuration
- Avoid mixing request matcher styles
Complete Configuration Example
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
private static final String[] PUBLIC_PATHS = {
"/openapi/openapi.yml",
"/v3/api-docs/**",
"/swagger-ui/**"
};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Convert public paths to Ant matchers
AntPathRequestMatcher[] publicMatchers = Arrays.stream(PUBLIC_PATHS)
.map(AntPathRequestMatcher::new)
.toArray(AntPathRequestMatcher[]::new);
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(requests ->
requests
.requestMatchers(publicMatchers).permitAll()
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
);
return http.build();
}
}
Conclusion
To resolve endpoint security issues in Spring Boot 3:
- Always wrap path patterns in
AntPathRequestMatcher
- Explicitly handle CORS with OPTIONS request matchers
- Disable CSRF for stateless APIs
- Stay updated - Spring Boot patches regularly address security configuration issues
- Consider multiple filter chains for complex access scenarios
The Lambda DSL in Spring Security 6 provides more type-safe configurations that eliminate common security flaws while maintaining readability. Always test your security configuration by verifying both public and private endpoints behave as expected.