I'm encountering an issue with WebSocket connectivity in my Spring web application. Locally, when I run the application, WebSocket connections are established successfully. However, upon deploying the application to a server on my domain, WebSocket connections fail to establish. The browser console displays the following error:
WebSocket connection to 'wss://merkeido.com/ws/776/1lq4jnfq/websocket' failed:
Additional Information:
What I've Tried:
Question: What could be causing the WebSocket connection to fail on deployment, specifically resulting in a 403 (Forbidden) error? Any insights or suggestions for troubleshooting would be greatly appreciated. Thank you!
WebSocket Configuration: Here is my WebSocket configuration class:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
HTML/JavaScript: And here is my HTML/JavaScript code for establishing WebSocket connections:
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script th:inline="javascript">
'use strict';
let stompClient = null;
let selectedUserId = null;
let userId = user_id;
connect();
function connect() {
const socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, onConnected, onError);
}
function onConnected() {
stompClient.subscribe(`/user/${userId+""}/queue/messages`, onMessageReceived);
stompClient.subscribe(`/user/public`, onMessageReceived);
// Register the connected user
stompClient.send("/app/user.addUser",
{},
JSON.stringify({id: userId+"", c_status: 'ONLINE'})
);
findAndDisplayConnectedUsers().then();
}
</script>
The actual error on my console:
Opening Web Socket...
websocket.js:6 WebSocket connection to 'wss://merkeido.com/ws/776/1lq4jnfq/websocket' failed:
e.exports @ websocket.js:6
/ws/776/in3mqa1z/xhr_streaming?t=1714645083452:1
Failed to load resource: the server responded with a status of 403 ()
stomp.min.js:8 Web Socket Opened...
stomp.min.js:8 >>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
I found a solution to the problem by making a few adjustments to my WebSocket and WebSecurity configurations.
WebSocket Configuration
First, I edited my endpoint in the WebSocketConfig class to ensure allowed origins:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// Ensure allowed origins
registry.addEndpoint("/ws")
.setAllowedOrigins("https://domain-name.com") // Replace with your client domain
.withSockJS();
}
}
Web Security Configuration
Next, I set up the WebSecurity configuration to handle CORS and CSRF:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/**").permitAll(); // Allow all requests temporarily
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain-name.com"); // Replace with your client domain
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
Explanation WebSocket Configuration:
The registerStompEndpoints method registers the /ws endpoint for STOMP over WebSocket and allows connections from https://domain-name.com using SockJS as a fallback option. Web Security Configuration:
The configure method disables CSRF and enables CORS, allowing all requests temporarily. The corsFilter bean configures CORS to allow credentials and requests from https://domain-name.com, along with all headers and methods. And just like that, it works. This setup ensures that your WebSocket connection and security configurations are correctly aligned, allowing your client to connect without issues.