Hello guys,
I have a weird problem with WebSocket in Spring Boot. Now, I am trying to authenticate the user when the user triggers an event from the client side (React, Typescript app); I have no issue if the web socket works with non-secure (HTTP, ws). However, when I deploy my application to the server in a secure way, my problem emerges that the user cannot be authenticated and, in some browsers, the web socket cannot connect to the server. I have no clue about this problem;
In summary, we encounter a problem like this. When opening a web socket connection from a React application, the user can authenticate in non-secure (http, ws). But in secure (wss, https), the user comes null and cannot authenticate. When the browser is changed, the connection is not realised. I need help finding a solution.
WebScoketMessageBrokerConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").addInterceptors(new HttpHandshakeInterceptor());
}
}
WebSocketEventListener
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
import java.util.Objects;
@Slf4j
@Component
@RequiredArgsConstructor
public class WebSocketEventListener {
@EventListener
public void handleWebSocketConnectListener(SessionConnectedEvent event) {
if(event.getUser() == null) {
log.info("[handleWebSocketConnectListener] WebSocket created for: user NULL, there is no user >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
return;
}
log.info("[handleWebSocketConnectListener] WebSocket created for: {}", decrypt(Objects.requireNonNull(event.getUser()).getName()));
}
@EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
if(event.getUser() == null) {
log.info("[handleWebSocketConnectListener] WebSocket created for: user NULL, there is no user >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
return;
}
String username = decrypt(Objects.requireNonNull(event.getUser()).getName());
log.info("[handleWebSocketDisconnectListener] Browser closed by: " + username);
}
}
DefaultSecurityConfig
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/ws/**").permitAll().anyRequest().authenticated())
http.csrf(CsrfConfigurer::disable);
http.cors(Customizer.withDefaults());
http.addFilterBefore(simpleAuthenticationFilter, OAuth2AuthorizationRequestRedirectFilter.class);
return http.build();
}
Yaml
server:
port: ${SERVER-PORT-SSL:9000} # HTTPS Port
http:
port: ${SERVER-PORT:9001} # HTTP Port
ssl:
enabled: true
key-store-type: JKS
key-store: classpath:certificate/backoffice.jks
key-store-password: backoffice
key-alias: backoffice
forward-headers-strategy: native
tomcat:
use-relative-redirects: true
remoteip:
remote-ip-header: x-forwarded-for
servlet:
session:
timeout: 10m
tracking-modes: cookie
cookie:
http-only: false
max-age: 10m
Frontend (Client-side):
import {Client} from "@stomp/stompjs";
const SOCKET_URL = 'wss://localhost:9000/ws?username=';
const connect = (userDetail: UserInfo) => {
console.log("[connect] connection will be established");
const onConnected = () => {
console.log("[connect] connection is established");
// client.subscribe('/queue/app', function (msg) {});
};
const onDisconnected = () => {
console.log("Disconnected!!");
};
const headers = {
Authorization: `Bearer ${token?.accessToken}`
};
const client = new Client({
brokerURL: SOCKET_URL + userDetail.email,
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
connectHeaders: headers,
disconnectHeaders: headers,
onConnect: onConnected,
onDisconnect: onDisconnected
});
client.activate();
};
HTTP (Non Secure), ws
As you can see, user is not null at all!
HTTPS (Secure), wss
As you can see, user is null in here!
Same browser but different profiles
As you can see, it cannot connect to server, when I open frontend application with different profile in same browser (Chrome)