I have recently added websocket configuration to my spring application, which is behind a Spring cloud gateway. There is also an angular app which communicates with my service through the gateway. The pronlem is that when i try co connect to it this is the error that i get in the mozilla: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8050/escrow-service/ws/info?t=1663539514537. (Reason: Multiple CORS header ‘Access-Control-Allow-Origin’ not allowed)
and this is in edge: Access to XMLHttpRequest at 'http://localhost:8050/some-service/ws/info?t=1663599778176' from origin 'http://localhost:4200' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:4200, http://localhost:4200', but only one is allowed.
When I try connecting directly to my app without going trough gateway, everything works as it should.
This is the code for the web-socket configuration:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/notification");
config.setApplicationDestinationPrefixes("/product");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
this is the code handling the cors in the gateway:
private CorsWebFilter corsFilter() {
/*
CORS requests are managed only if headers Origin and Access-Control-Request-Method are available on OPTIONS requests
(this filter is simply ignored in other cases).
This filter can be used as a replacement for the @Cors annotation.
*/
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOriginPatterns(Collections.singletonList("*"));
config.addAllowedHeader(ORIGIN);
config.addAllowedHeader(CONTENT_TYPE);
config.addAllowedHeader(ACCEPT);
config.addAllowedHeader(AUTHORIZATION);
config.addAllowedHeader(CONNECTION);
config.addAllowedHeader(ACCEPT_ENCODING);
config.addAllowedHeader(USER_AGENT);
config.addAllowedHeader(CONTENT_LENGTH);
config.addAllowedHeader(HOST);
config.addAllowedHeader("Postman-Token");
config.addAllowedHeader(COOKIE);
config.addAllowedMethod(GET);
config.addAllowedMethod(PUT);
config.addAllowedMethod(POST);
config.addAllowedMethod(OPTIONS);
config.addAllowedMethod(DELETE);
config.addAllowedMethod(PATCH);
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
And this is the angular code for calling the endpoints:
connect() {
console.log("Initialize WebSocket Connection");
const _this = this;
let socket = new SockJS(this.appComponent.connectionUrl);
let destinationUrl=this.appComponent.destinationUrl;
_this.stompClient = Stomp.over(socket);
_this.stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
_this.stompClient.subscribe(destinationUrl, function (greeting: any) {
let obj = JSON.parse(greeting.body);
console.log(obj);
_this.onMessageReceived(obj);
});
});
};
After commenting with K.Nicholas, I found the solution and added it programatically as everywhere, there was only declarative solution.
@Bean
public RouteLocator applicationRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/my-service/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec
.rewritePath("/escrow-service/(?<remaining>.*)", "/${remaining}")
.dedupeResponseHeader("Access-Control-Allow-Origin","RETAIN_UNIQUE")
.dedupeResponseHeader("Access-Control-Allow-Credentials","RETAIN_UNIQUE")
.filter(customLoggingFilter))
.uri("lb://my-service/"))
.build();
}
the key was in adding
.dedupeResponseHeader("Access-Control-Allow-Origin","RETAIN_UNIQUE")
.dedupeResponseHeader("Access-Control-Allow-Credentials","RETAIN_UNIQUE")
these headers so that the duplicates are removed.