This project demonstrates a bug in Spring Security where CORS configuration silently fails when the CorsConfigurationSource bean name differs from the expected "corsConfigurationSource".
When using .cors(Customizer.withDefaults()) in Spring Security configuration, the framework looks for a bean with the exact name "corsConfigurationSource". If a CorsConfigurationSource bean is registered with a different name (e.g., when using @Profile annotations with different method names), Spring Security silently falls back to DefaultCorsProcessor, which rejects all preflight requests.
- Java 17 or higher
- Gradle (or use the wrapper:
./gradlew)
spring-security-cors-issue/
├── build.gradle
├── README.md
└── src/
├── main/
│ ├── java/com/example/corsissue/
│ │ ├── CorsIssueApplication.java # Main application
│ │ ├── config/
│ │ │ └── SecurityConfig.java # Bug demonstration here!
│ │ └── controller/
│ │ └── TestController.java # Simple REST endpoint
│ └── resources/
│ └── application.yml
└── test/
└── java/com/example/corsissue/
└── CorsIssueTest.java # Test that demonstrates the bug
./gradlew testThe test testPreflightRequestFails() will fail because:
- The application is running with
@ActiveProfiles("test")(non-prod profile) - This activates the bean method
testCorsConfigurationSource()instead ofcorsConfigurationSource() - Spring Security's
CorsConfigurer.getCorsFilter()specifically looks for a bean named"corsConfigurationSource" - Since it can't find that bean, it falls back to
DefaultCorsProcessor DefaultCorsProcessorrejects preflight OPTIONS requests with "Invalid CORS request" (403 Forbidden)
To see that the CORS configuration works when the bean is named correctly:
- Open
src/test/java/com/example/corsissue/CorsIssueTest.java - Change
@ActiveProfiles("test")to@ActiveProfiles("prod") - Run the tests again
Now the preflight request should succeed because the bean is named corsConfigurationSource.
You can also test manually using curl:
# Start the application with test profile
./gradlew bootRun --args='--spring.profiles.active=test'
# In another terminal, send a preflight request
curl -i -X OPTIONS http://localhost:8080/api/test \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type"You'll receive a 403 Forbidden response with the message "Invalid CORS request".
@Bean
@Profile("!prod")
public CorsConfigurationSource testCorsConfigurationSource() { // Wrong bean name!
// Valid CORS configuration
// ...
}In org.springframework.security.config.annotation.web.configurers.CorsConfigurer:
private CorsFilter getCorsFilter(ApplicationContext context) {
// ...
boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
if (containsCorsSource) {
CorsConfigurationSource configurationSource = context.getBean(
CORS_CONFIGURATION_SOURCE_BEAN_NAME, // Hardcoded to "corsConfigurationSource"
CorsConfigurationSource.class
);
return new CorsFilter(configurationSource);
}
// ...
}The constant CORS_CONFIGURATION_SOURCE_BEAN_NAME is "corsConfigurationSource". Since our test profile bean is named testCorsConfigurationSource, it's not detected.
@Bean(name = "corsConfigurationSource")
@Profile("!prod")
public CorsConfigurationSource testCorsConfigurationSource() {
// ...
}@Autowired
private CorsConfigurationSource corsConfigurationSource;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource))
// ...
}One of the following should happen:
- Preferred: Spring Security should detect
CorsConfigurationSourcebeans by type, not just by name - Alternative: Log a clear warning or throw an exception when CORS is enabled but no valid configuration source is found
- Minimum: Update documentation to explicitly mention the required bean name
This sample project is created to demonstrate the issue reported at: [Link to GitHub issue when created]
- Spring Boot: 3.2.0
- Spring Security: (included with Spring Boot)
- Java: 17
- Build Tool: Gradle
This is a minimal reproducible sample for demonstration purposes only.