官方文檔-spring websocket/stomp
官方文檔-spring sessionjavascript
WebsocketSTOMPConfig:html
@Configuration @EnableWebSocketMessageBroker @ConditionalOnWebApplication public class WebsocketSTOMPConfig extends AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> { /** * AbstractSessionWebSocketMessageBrokerConfigurer實現了在handshake時獲取httpsession,而且每次websocket消息發生時也刷新了httpsession的時間。同時在websocket session中加入了SPRING.SESSION.ID字段。 */ @Override protected void configureStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws") // 在握手時就得到user,判斷是否登陸。 .addInterceptors(new SessionAuthHandshakeInterceptor()) .setHandshakeHandler(new DefaultHandshakeHandler(){ @Override protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) { return new MyPrincipal((User) attributes.get("user")); } }) .setAllowedOrigins("http://127.0.0.1:8081"); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.setInterceptors(new ChannelInterceptorAdapter() { @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { System.out.println("recv : "+message); StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); User user = (User)accessor.getSessionAttributes().get("user"); return super.preSend(message, channel); } }); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { registration.setInterceptors(new ChannelInterceptorAdapter() { @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { System.out.println("send: "+message); return super.preSend(message, channel); } }); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { // 這是配置到 @MessageMapping Controller config.setApplicationDestinationPrefixes("/app"); // 直接到broker message handler config.enableSimpleBroker("/topic", "/queue"); } class MyPrincipal implements Principal{ private User user; public MyPrincipal(User user) { this.user = user; } @Override public String getName() { return String.valueOf(user.getId()); } } }
SessionAuthHandshakeInterceptor: 這是自定義的握手攔截,獲取已登陸的User前端
public class SessionAuthHandshakeInterceptor implements HandshakeInterceptor { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { HttpSession session = getSession(request); if(session==null || session.getAttribute("user")==null){ logger.error("websocket權限拒絕"); return false; } attributes.put("user",session.getAttribute("user")); return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } // 參考 HttpSessionHandshakeInterceptor private HttpSession getSession(ServerHttpRequest request) { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request; return serverRequest.getServletRequest().getSession(false); } return null; } }
而後在configureClientInboundChannel中能夠經過如下得到User:html5
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); User user = (User)accessor.getSessionAttributes().get("user");
還有一種得到User的方式,能夠在WebsocketSTOMPConfig中注入SessionRepository來得到springsession:java
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); sessionRepository.getSession((String) accessor.getSessionAttributes().get("SPRING.SESSION.ID"))
Spring提供了"/user"前綴專門用於精準推送單個用戶。web
client 訂閱 "/user/queue/msg",spring經過UserDestinationMessageHandler從新設置client的訂閱地址,使與"/user/{username}/queue/msg"綁定。spring
前端代碼:websocket
var url = "ws://"+window.location.host+"/project/ws"; var client = Stomp.client(url); client.connect({}, function (message) { const subscription = client.subscribe("/user/queue/msg", function () {}); client.send("/app/test2", {}, "Hello, STOMP"); },function (err) { alert(err); });
Controller:session
@Controller public class MsgController { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/test2") public void test(String str, Principal principal){ simpMessagingTemplate.convertAndSendToUser(principal.getName(),"/queue/msg","haha2"); } }
在WebsocketSTOMPConfig中的configureStompEndpoints配置:app
registry.addEndpoint("/ws") .addInterceptors(new SessionAuthHandshakeInterceptor()) .setHandshakeHandler(new DefaultHandshakeHandler(){ @Override protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) { return new MyPrincipal((User) attributes.get("user")); } });