Search code examples
javawebsocketspring-websocketstompsockjs

How to fix the 'Access-Control-Allow-Origin' error when connecting to a WebSocket?


I am working on a SockJS and WebSocket type of project and for the frontend I am using React and on the backend the spring's implementation of WebSockets. But I am getting a CORS error when I try to connect to my WebSocket.

Access to XMLHttpRequest at 'http://localhost:8080/ws/info?t=1579096675068' from origin 'http://localhost:3000' has been blocked by CORS policy: 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

In the Java project I have included this CORS configuration:

@Bean
CorsConfigurationSource corsConfigurationSource() {
     final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
     final CorsConfiguration configuration = new CorsConfiguration();
     configuration.applyPermitDefaultValues();
     configuration.setExposedHeaders(Arrays.asList("Authorization"));
     configuration.addAllowedOrigin("*");
     configuration.addAllowedMethod("*");
     configuration.addAllowedHeader("*");
     source.registerCorsConfiguration("/**", configuration);
     return source;
}

And as for the configure method on the WebSecurity class I have included this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable().authorizeRequests()
             .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
             .anyRequest().authenticated()
             .and()
             .addFilter(new JWTAuthenticationFilter(authenticationManager()))
             .addFilter(new JWTAuthorizationFilter(authenticationManager()))
             .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}

I add my socket endpoint like this:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}

On the frontend I connect to my WebSocket with a connect method:

connect = () => {
  const Stomp = require("stompjs");
  let SockJS = require("sockjs-client");
  SockJS = new SockJS("http://localhost:8080/ws");
  stompClient = Stomp.over(SockJS);
  stompClient.connect({}, this.onConnected, this.onError);
};

I have tried setting the URL on the registerStompEndpoints method as http://localhost:3000 explicitly, but to no effect. Also added a proxy on my package.json in the frontend to http://localhost:8080/, but it still gives the same error. Is there anything I need to do on my corsConfigurationSource to get this working?

UPDATE

When I do the following configure method it solves the WebSocket problem as it can connect to it, but I lose the ability to visit other routes of my application because it gives another error.

protected void configure(HttpSecurity http) throws Exception {
     http.authorizeRequests()
             .antMatchers("/ws").permitAll()
             .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
             .and()
             .addFilter(new JWTAuthenticationFilter(authenticationManager()))
             .addFilter(new JWTAuthorizationFilter(authenticationManager()))
     .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().csrf().disable();
}

The other error:

Access to XMLHttpRequest at 'http://localhost:8080/auth/me' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Solution

  • @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.cors().and()
                  .authorizeRequests()
                  .antMatchers("/ws/**").permitAll()
                  .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
                  .anyRequest().authenticated()
                  .and()
                  .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                  .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                  .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                  .and().csrf().disable();
    }
    
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
    
            final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            final CorsConfiguration configuration = new CorsConfiguration();
            configuration.setExposedHeaders(Arrays.asList("Authorization"));
            configuration.addAllowedOrigin("*");
            configuration.addAllowedMethod("*");
            configuration.addAllowedHeader("*");
            configuration.applyPermitDefaultValues();
            source.registerCorsConfiguration("/api/**", configuration);
            source.registerCorsConfiguration("/auth/*", configuration);
            source.registerCorsConfiguration("/login", configuration);
            return source;
    }
    

    I ended up using this as my WebSecurity. To sum up I added .antMatchers("/ws/**").permitAll() on my configure method. So I permit all requests coming to my WebSocket endpoint and also explicitly registering CORS configuration on all my other routes except the /ws routes on the corsConfigurationSource method. Hope this helps.