Spring Security CORS Error (WebMvcConfigurer / corsConfigurationSource)
This post was migrated from Tistory. You can find the original here.
Background needed to understand this post:
Where the problem started
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:4000")
.allowedMethods("GET","POST","PUT","DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.exposedHeaders(JwtUtil.AUTHORIZATION_HEADER);
}
}
1
2
3
4
5
6
7
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
log.info("Login attempt");
try {
UserLoginDto requestDto = new ObjectMapper().readValue(request.getInputStream(), UserLoginDto.class);
System.out.println(requestDto);
....
When CORS was handled by implementing WebMvcConfigurer as shown above, request.getInputStream() ended up empty in the UsernamePasswordAuthenticationFilter of the Security filter chain.
Checking the actual network traffic confirmed this:
The CORS policy configured on the server wasn’t being applied to the preflight headers, and comparing the preflight request and response showed a mismatch — which was deemed unsafe, so a CORS error was thrown.
Let’s check the official Spring Security docs.
CORS must be processed before Spring Security, because the pre-flight request does not contain any cookies (that is, the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it.
In other words, CORS needs to be handled before it reaches Security. If Security runs first and receives a preflight request with no cookies or credentials, it treats the request as unauthenticated and rejects it. Spring Security provides an easy way to handle CORS first.
1
2
3
4
5
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf((csrf) -> csrf.disable());
http.cors(withDefaults());
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://localhost:4000"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.addExposedHeader(JwtUtil.AUTHORIZATION_HEADER);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
By setting cors(withDefaults()) in the SecurityFilterChain and writing a corsConfigurationSource bean as shown above, you can let Security handle CORS for you.
After removing WebMvcConfigurer and switching to Security’s built-in CORS handling, the configured CORS policy now shows up in the preflight response headers, and the actual request no longer throws a CORS error.



