Недавно я участвовал в проекте, где нам нужно было захватить идентификатор сеанса http для запроса websocket — причина была в том, чтобы определить количество сеансов websocket, использующих один и тот же базовый сеанс http.
Способ сделать это основан на примере использования нового модуля весенней сессии и описан здесь .
Хитрость в захвате идентификатора http-сессии заключается в том, чтобы понять, что перед установкой соединения веб-сокета между браузером и сервером существует фаза рукопожатия, согласованная по протоколу http, и идентификатор сеанса передается на сервер во время этой фазы квитирования.
Поддержка Spring Websocket предоставляет хороший способ зарегистрировать HandShakeInterceptor , который можно использовать для захвата идентификатора сеанса http и установки его в заголовках суб-протокола (обычно STOMP). Во-первых, это способ получить идентификатор сеанса и установить его в заголовок:
01
02
03
04
05
06
07
08
09
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) { } } |
И чтобы зарегистрировать этот HandshakeInterceptor с поддержкой Spring Websocket:
01
02
03
04
05
06
07
08
09
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(); } } |
Теперь, когда идентификатор сеанса является частью заголовков STOMP, его можно получить как заголовок STOMP, ниже приведен пример, где он захватывается при регистрации подписок на сервере:
01
02
03
04
05
06
07
08
09
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()); } } |
или его можно получить из метода контроллера, обрабатывающего сообщения websocket, в качестве параметра MessageHeaders :
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()); } |
- Вот полный рабочий пример, который реализует этот шаблон.
Ссылка: | Приложение для загрузки веб-сокетов на основе весенней загрузки и получение идентификатора сеанса http от нашего партнера по JCG Биджу Кунджуммена из блога all and sundry. |