我最近参与了一个项目,我们需要捕获Websocket请求的http会话ID –原因是要确定使用相同基础HTTP会话的Websocket会话的数量。
这样做的方法基于利用新的spring-session模块的示例,并在此处进行描述。
捕获http会话ID的技巧是理解在浏览器和服务器之间建立Websocket连接之前,有一个通过HTTP协商的握手阶段,并且会话ID在此握手阶段传递给服务器。
Spring Websocket支持提供了一种注册HandShakeInterceptor的好方法,该方法可用于捕获http会话ID并将其设置在子协议(通常为STOMP)标头中。 首先,这是捕获会话ID并将其设置为标头的方法:
public class HttpSessionIdHandshakeInterceptor implements HandshakeInterceptor {@Overridepublic 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) {}
}
并在Spring Websocket支持下注册此HandshakeInterceptor:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketDefaultConfig extends AbstractWebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/topic/", "/queue/");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/chat").withSockJS().setInterceptors(httpSessionIdHandshakeInterceptor());}@Beanpublic HttpSessionIdHandshakeInterceptor httpSessionIdHandshakeInterceptor() {return new HttpSessionIdHandshakeInterceptor();}}
现在,会话ID是STOMP标头的一部分,可以将其作为STOMP标头抓取,以下是在向服务器注册订阅时抓取的示例:
@Component
public class StompSubscribeEventListener implements ApplicationListener<SessionSubscribeEvent> {private static final Logger logger = LoggerFactory.getLogger(StompSubscribeEventListener.class);@Overridepublic void onApplicationEvent(SessionSubscribeEvent sessionSubscribeEvent) {StompHeaderAccessor headerAccessor = StompHeaderAccessor.wrap(sessionSubscribeEvent.getMessage());logger.info(headerAccessor.getSessionAttributes().get("HTTPSESSIONID").toString());}
}
或者可以从将websocket消息作为MessageHeaders参数处理的控制器方法中获取:
@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());}
- 这是实现此模式的完整工作示例。
翻译自: https://www.javacodegeeks.com/2014/11/spring-boot-based-websocket-application-and-capturing-http-session-id.html