The way to do this is based on a sample utilizing the new spring-session module and is described here.
The trick to capturing the http session id is in understanding that before a websocket connection is established between the browser and the server, there is a handshake phase negotiated over http and the session id is passed to the server during this handshake phase.
Spring Websocket support provides a nice way to register a HandShakeInterceptor, which can be used to capture the http session id and set this in the sub-protocol(typically STOMP) headers. First, this is the way to capture the session id and set it to a header:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class HttpSessionIdHandshakeInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession session = servletRequest.getServletRequest().getSession( false ); if (session != null ) { attributes.put( "HTTPSESSIONID" , session.getId()); } } return true ; } public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { } } |
And to register this HandshakeInterceptor with Spring Websocket support:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Configuration @EnableWebSocketMessageBroker public class WebSocketDefaultConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker( "/topic/" , "/queue/" ); config.setApplicationDestinationPrefixes( "/app" ); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint( "/chat" ).withSockJS().setInterceptors(httpSessionIdHandshakeInterceptor()); } @Bean public HttpSessionIdHandshakeInterceptor httpSessionIdHandshakeInterceptor() { return new HttpSessionIdHandshakeInterceptor(); } } |
Now that the session id is a part of the STOMP headers, this can be grabbed as a STOMP header, the following is a sample where it is being grabbed when subscriptions are registered to the server:
1 2 3 4 5 6 7 8 9 10 11 | @Component public class StompSubscribeEventListener implements ApplicationListener<SessionSubscribeEvent> { private static final Logger logger = LoggerFactory.getLogger(StompSubscribeEventListener. class ); @Override public void onApplicationEvent(SessionSubscribeEvent sessionSubscribeEvent) { StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(sessionSubscribeEvent.getMessage()); logger.info(headerAccessor.getSessionAttributes().get( "HTTPSESSIONID" ).toString()); } } |
or it can be grabbed from a controller method handling websocket messages as a MessageHeaders parameter:
1 2 3 4 5 | @MessageMapping ( "/chats/{chatRoomId}" ) public void handleChat( @Payload ChatMessage message, @DestinationVariable ( "chatRoomId" ) String chatRoomId, MessageHeaders messageHeaders, Principal user) { logger.info(messageHeaders.toString()); this .simpMessagingTemplate.convertAndSend( "/topic/chats." + chatRoomId, "[" + getTimestamp() + "]:" + user.getName() + ":" + message.getMessage()); } |
Here is a complete working sample which implements this pattern.
good post. but can you activate the link?
ReplyDelete