Keywords: CORS | Spring Boot | WebSocket | Angular | Auth0
Abstract: In Spring Boot WebSocket setups with Angular clients, a common error occurs when CORS is configured with wildcard origins while credentials are included. This article explains the root cause and provides a step-by-step solution using a custom CORSFilter to properly set allowed origins and handle cross-origin requests securely.
Problem Overview
When integrating real-time messaging using WebSocket with Spring Boot and Angular, developers often encounter CORS errors, especially when authentication credentials are involved. A common error message is: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. This occurs because browsers enforce strict security policies for cross-origin requests with credentials.
Understanding CORS with Credentials
CORS (Cross-Origin Resource Sharing) allows web applications to make requests to different domains. However, when credentials such as cookies or authorization headers are included, the server must specify exact origins instead of using the wildcard *. This is a security measure to prevent unauthorized access.
Why WebSocketConfig Falls Short
In Spring Boot, configuring WebSocket endpoints with setAllowedOrigins might not handle all CORS aspects, particularly for preflight requests or when credentials are involved. As seen in the provided example, even with setAllowedOrigins("http://localhost:4200"), the error persists because the underlying HTTP requests during WebSocket handshake might not be properly configured.
Solution: Implementing a CORSFilter
To resolve this, a custom CORSFilter can be created to intercept all HTTP requests and set appropriate CORS headers. This filter ensures that specific origins are allowed and credentials are permitted.
public class CORSFilter implements Filter {
private final List<String> allowedOrigins = Arrays.asList("http://localhost:4200");
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String origin = request.getHeader("Origin");
if (allowedOrigins.contains(origin)) {
response.setHeader("Access-Control-Allow-Origin", origin);
} else {
response.setHeader("Access-Control-Allow-Origin", "");
}
response.setHeader("Vary", "Origin");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, X-CSRF-TOKEN");
}
chain.doFilter(req, res);
}
// Other methods: init and destroy can be implemented if needed
}Integrating the CORSFilter in Spring Boot
To make the filter effective, it needs to be registered in the Spring Security configuration. In SecurityConfig, add the filter before other filters to ensure CORS headers are set early in the request lifecycle.
@Bean
public CORSFilter corsFilter() {
return new CORSFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(corsFilter(), SessionManagementFilter.class)
.authorizeRequests().anyRequest().permitAll();
http.csrf().disable();
}Testing and Debugging
After implementation, test the connection from the Angular client. Note that some browsers, like Chrome, have stricter enforcement, so ensure that the Access-Control-Allow-Origin header matches the exact origin. Debugging can be done by inspecting network requests in the browser's developer tools.
Conclusion
Properly configuring CORS in Spring Boot WebSocket applications requires a comprehensive approach beyond simple endpoint configuration. By implementing a custom CORSFilter, developers can specify allowed origins and enable credentials, ensuring secure and functional cross-origin communication. This solution addresses the common error and provides a robust foundation for real-time applications with authentication.