I have google searched so many article and ask question on Stack overflow, but I still not resolve this problem, so I hope someone in yours could help me have a look. Thanks! I have push my code into github, and the eureka and mysql is mine, you can clone it and run it directly:

auth-service websocket-service vue-websocket-demo

Get the jwt access_toen 1. you can get directly use this request with post method:

http://120.27.218.194:8081/oauth/token?grant_type=password&client_id=test-client-id&client_secret=123456&scope=all&username=elvis%40gmail.com&password=elvis2020
  1. use this one
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExODg4NDUsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI0OTlkYmQyMC1iMGFmLTQzNmItOWNiNS05YTU4NjdkNjQxOTQiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.UfN7GZikIFYp3j3htY-9OQ9XdCl0-gPNbAf6eZq3oBk
  1. clone the auth-service, run it in local and get one

1. scenes to be used

  • When connect WebSocket is permitAll
  • Then Subscribe /user/queue/userInfo need user authenticated
  • After subscribe success, send to /app/user/info need authenticated(Because I need get userInfo ), and push message to current user subscribe queue /user/queue/userInfo

2. What problem I encountered

  • Connect WebSocket success
  • Subscribe /user/queue/userInfo success
  • Send to /app/user/info success
  • At last I cannot receive the message return from server.

2.1 Details description after I trace the source code

1) when subscribe /user/queue/userInfo with the jwtToken in header, I get the jwtToken, then get Principle user object from auth server with jwtToken, then set user in StompHeaderAccessor. At last, the user subscribe lookupDestination is /queue/userInfo/user{sessionId} 2) Then send /app/user/userInfo with jwtToken in header, after handle the return value, the final send destination is /user/{Principle.getName()}/queue/userInfo.

the user never subscribe this queue: /user/{Principle.getName()}/queue/userInfo, and the message cannot send to user. the trace log : o.s.m.s.u.UserDestinationMessageHandler : No active sessions for user destination: /user/1/queue/userInfo

3. Related Code

3.1 WebSocketConfig

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Autowired
    private  CustomClientInboundChannelInterceptor customClientInboundChannelInterceptor;


    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(customClientInboundChannelInterceptor);
    }


    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/sockJs").setAllowedOrigins("*").withSockJS();
    }


    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {

        config.enableSimpleBroker("/topic", "/queue");
        config.setApplicationDestinationPrefixes("/app","/user");
    }

}

3.2 WebSocketSecurityConfig

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        messages
                .simpTypeMatchers(SimpMessageType.CONNECT, SimpMessageType.HEARTBEAT, SimpMessageType.UNSUBSCRIBE, SimpMessageType.DISCONNECT).permitAll()
                .simpDestMatchers("/user/**").authenticated()
                .simpSubscribeDestMatchers("/user/**").authenticated();

    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }

}

3.3 CustomClientInboundChannelInterceptor

This Interceptor is according to the spring reference documentation:websocket-stomp-authentication

@Component
public class CustomClientInboundChannelInterceptor implements ChannelInterceptor {

    private final AuthServiceClient authServiceClient;

    @Autowired
    public CustomClientInboundChannelInterceptor(AuthServiceClient authServiceClient) {
        this.authServiceClient = authServiceClient;
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.SUBSCRIBE.equals(accessor.getCommand()) ||
                StompCommand.SEND.equals(accessor.getCommand())
        ) {
            String jwtToken = accessor.getFirstNativeHeader("Authorization");
            log.debug("webSocket token is {}", jwtToken);
            if (!StringUtils.isEmpty(jwtToken)) {

                PassUser passUser = authServiceClient.userInfo(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(passUser);

                accessor.setUser(passUser);
                // I try to remove above code : accessor.setUser(passUser);
                // Then SecurityContextChannelInterceptor line 123,
                // it will take user from message header with key "simpUser"
                // so if I remove , the user will change to anonymous, and request will be AccessDeniedException
            }
        }
        return message;
    }

}

3.3 Subscribe and Send from front-end js

       connectWebSocket() {
            let socket = new SockJS(this.baseUrl+'/sockJs');
            this.stompClient = Stomp.over(socket);

            this.stompClient.connect({}, function (frame) {
              console.log('Connected: ' + frame)
            });
          }

        subscribeLoginUserMessage(){

          var headers = {
                'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTA2OTk0MTYsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI0NzNjOTVlYy1iNjUwLTQ2OTUtOTVhMy00ODYzZjRjOWZjOGQiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.YH6yChvdATn1vMonbc0OhSgI_kTi3KeIzgCldypInLg'
            };

            this.stompClient.subscribe("/user/queue/userInfo", function (e) {
              let data = JSON.parse(e.body)
              console.log('login user p2p message response', data)

            }, headers);
        }

       userInfo(){
          let data = {}
          var headers = {
                'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTA2OTk0MTYsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI0NzNjOTVlYy1iNjUwLTQ2OTUtOTVhMy00ODYzZjRjOWZjOGQiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.YH6yChvdATn1vMonbc0OhSgI_kTi3KeIzgCldypInLg'
            }

          this.stompClient.send("/app/user/info", headers, JSON.stringify(data));
        }

3.4 Controller method which handle the send request

    @MessageMapping("/user/info")
    @SendToUser(value = "/queue/userInfo", broadcast = false)
    public String userInfo(@AuthenticationPrincipal PassUser passUser) {
        log.info("push user info":passUser={}", JsonUtil.toJsonString(passUser));
        return JsonUtil.toJsonString(passUser);
    }

or

@MessageMapping("/user/info")
    public void userInfo2(Principal passUser) {
        log.info("get user info:passUser={}", JsonUtil.toJsonString(passUser));
        simpMessagingTemplate.convertAndSendToUser(passUser.getName(), "/queue/userInfo", JsonUtil.toJsonString(passUser));
    }

3.5 Connect 、Subscribe、Send trace log

2020-05-30 16:00:32.907 DEBUG 5503 --- [nio-8082-exec-7] o.s.w.s.s.t.h.DefaultSockJsService       : Processing transport request: GET http://127.0.0.1:8082/sockJs/info?t=1590825631897
2020-05-30 16:00:32.918 TRACE 5503 --- [nio-8082-exec-8] o.s.w.s.s.s.WebSocketHandlerMapping      : Mapped to HandlerExecutionChain with [org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler@171a6fd1] and 1 interceptors
2020-05-30 16:00:32.919 DEBUG 5503 --- [nio-8082-exec-8] o.s.w.s.s.t.h.DefaultSockJsService       : Processing transport request: GET http://127.0.0.1:8082/sockJs/624/s2bz5lbp/websocket
2020-05-30 16:00:32.920 TRACE 5503 --- [nio-8082-exec-8] o.s.w.s.s.s.DefaultHandshakeHandler      : Processing request http://127.0.0.1:8082/sockJs/624/s2bz5lbp/websocket with headers=[host:"127.0.0.1:8082", connection:"Upgrade", pragma:"no-cache", cache-control:"no-cache", user-agent:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36", upgrade:"websocket", origin:"http://localhost:8080", sec-websocket-version:"13", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9", sec-websocket-key:"OM9hoc6/P6BkOGms4TiARg==", sec-websocket-extensions:"permessage-deflate; client_max_window_bits"]
2020-05-30 16:00:32.920 TRACE 5503 --- [nio-8082-exec-8] o.s.w.s.s.s.DefaultHandshakeHandler      : Upgrading to WebSocket, subProtocol=null, extensions=[]
2020-05-30 16:00:32.924 DEBUG 5503 --- [nio-8082-exec-8] s.w.s.h.LoggingWebSocketHandlerDecorator : New WebSocketServerSockJsSession[id=s2bz5lbp]
2020-05-30 16:00:32.924 TRACE 5503 --- [nio-8082-exec-8] o.s.w.s.adapter.NativeWebSocketSession   : Sending TextMessage payload=[o], byteCount=1, last=true], StandardWebSocketSession[id=ec86194d-cc25-b73e-b5cc-3a5e123e946a, uri=ws://127.0.0.1:8082/sockJs/624/s2bz5lbp/websocket]
2020-05-30 16:00:32.925 TRACE 5503 --- [nio-8082-exec-8] s.w.s.s.t.s.WebSocketServerSockJsSession : Scheduled heartbeat in session s2bz5lbp
2020-05-30 16:00:32.927 TRACE 5503 --- [nio-8082-exec-9] s.w.s.h.LoggingWebSocketHandlerDecorator : Handling TextMessage payload=[CONNECT
ac..], byteCount=56, last=true] in WebSocketServerSockJsSession[id=s2bz5lbp]
2020-05-30 16:00:32.928 TRACE 5503 --- [nio-8082-exec-9] o.s.messaging.simp.stomp.StompDecoder    : Decoded CONNECT {accept-version=[1.1,1.0], heart-beat=[10000,10000]} session=null
2020-05-30 16:00:32.928 TRACE 5503 --- [nio-8082-exec-9] o.s.w.s.m.StompSubProtocolHandler        : From client: CONNECT session=s2bz5lbp
2020-05-30 16:00:32.930 DEBUG 5503 --- [boundChannel-38] o.s.m.s.b.SimpleBrokerMessageHandler     : Processing CONNECT session=s2bz5lbp
2020-05-30 16:00:32.932 TRACE 5503 --- [tboundChannel-7] o.s.messaging.simp.stomp.StompEncoder    : Encoding STOMP CONNECTED, headers={version=[1.1], heart-beat=[0,0]}
2020-05-30 16:00:32.932 TRACE 5503 --- [tboundChannel-7] s.w.s.s.t.s.WebSocketServerSockJsSession : Cancelling heartbeat in session s2bz5lbp
2020-05-30 16:00:32.933 TRACE 5503 --- [tboundChannel-7] s.w.s.s.t.s.WebSocketServerSockJsSession : Preparing to write SockJsFrame content='a["CONNECTED\nversion:1.1\nheart-beat:0,0\n\n\u0000"]'
2020-05-30 16:00:32.933 TRACE 5503 --- [tboundChannel-7] s.w.s.s.t.s.WebSocketServerSockJsSession : Writing SockJsFrame content='a["CONNECTED\nversion:1.1\nheart-beat:0,0\n\n\u0000"]'
2020-05-30 16:00:32.933 TRACE 5503 --- [tboundChannel-7] o.s.w.s.adapter.NativeWebSocketSession   : Sending TextMessage payload=[a["CONNECT..], byteCount=53, last=true], StandardWebSocketSession[id=ec86194d-cc25-b73e-b5cc-3a5e123e946a, uri=ws://127.0.0.1:8082/sockJs/624/s2bz5lbp/websocket]
2020-05-30 16:00:32.934 TRACE 5503 --- [tboundChannel-7] s.w.s.s.t.s.WebSocketServerSockJsSession : Scheduled heartbeat in session s2bz5lbp
2020-05-30 16:00:34.839 TRACE 5503 --- [nio-8082-exec-1] s.w.s.h.LoggingWebSocketHandlerDecorator : Handling TextMessage payload=[SUBSCRIBE
..], byteCount=346, last=true] in WebSocketServerSockJsSession[id=s2bz5lbp]
2020-05-30 16:00:34.839 TRACE 5503 --- [nio-8082-exec-1] o.s.messaging.simp.stomp.StompDecoder    : Decoded SUBSCRIBE {client-id=[my-client-id], Authorization=[Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExNzIwMTUsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI4Y2JlNjBlMi05NjI0LTQ1YmQtOWJjZS0wZjk5ZDRkNGViNmUiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.jNWNuw3EXlm8nFPnmoJEZ0aWmpxfBM9j_CI-Pw2Ds64], id=[sub-0], destination=[/user/queue/userInfo]} session=null
2020-05-30 16:00:34.839 TRACE 5503 --- [nio-8082-exec-1] o.s.w.s.m.StompSubProtocolHandler        : From client: SUBSCRIBE /user/queue/userInfo id=sub-0 session=s2bz5lbp
2020-05-30 16:00:34.839 DEBUG 5503 --- [nio-8082-exec-1] .i.CustomClientInboundChannelInterceptor : webSocket token is Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExNzIwMTUsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI4Y2JlNjBlMi05NjI0LTQ1YmQtOWJjZS0wZjk5ZDRkNGViNmUiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.jNWNuw3EXlm8nFPnmoJEZ0aWmpxfBM9j_CI-Pw2Ds64
2020-05-30 16:00:34.934 DEBUG 5503 --- [boundChannel-40] .WebSocketAnnotationMethodMessageHandler : Searching methods to handle SUBSCRIBE /user/queue/userInfo id=sub-0 session=s2bz5lbp, lookupDestination='/queue/userInfo'
2020-05-30 16:00:34.934 TRACE 5503 --- [boundChannel-42] o.s.m.s.u.UserDestinationMessageHandler  : Translated /user/queue/userInfo -> [/queue/userInfo-users2bz5lbp]
2020-05-30 16:00:34.934 DEBUG 5503 --- [boundChannel-40] .WebSocketAnnotationMethodMessageHandler : No matching message handler methods.
2020-05-30 16:00:34.934 DEBUG 5503 --- [boundChannel-42] o.s.m.s.b.SimpleBrokerMessageHandler     : Processing SUBSCRIBE destination=/queue/userInfo-users2bz5lbp subscriptionId=sub-0 session=s2bz5lbp user=1 payload=byte[0]
2020-05-30 16:00:38.122 TRACE 5503 --- [io-8082-exec-10] s.w.s.h.LoggingWebSocketHandlerDecorator : Handling TextMessage payload=[SEND
clien..], byteCount=345, last=true] in WebSocketServerSockJsSession[id=s2bz5lbp]
2020-05-30 16:00:38.122 TRACE 5503 --- [io-8082-exec-10] o.s.messaging.simp.stomp.StompDecoder    : Decoded SEND {client-id=[my-client-id], Authorization=[Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExNzIwMTUsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI4Y2JlNjBlMi05NjI0LTQ1YmQtOWJjZS0wZjk5ZDRkNGViNmUiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.jNWNuw3EXlm8nFPnmoJEZ0aWmpxfBM9j_CI-Pw2Ds64], destination=[/app/user/info], content-length=[2]} session=null
2020-05-30 16:00:38.123 TRACE 5503 --- [io-8082-exec-10] o.s.w.s.m.StompSubProtocolHandler        : From client: SEND /app/user/info session=s2bz5lbp
2020-05-30 16:00:38.123 DEBUG 5503 --- [io-8082-exec-10] .i.CustomClientInboundChannelInterceptor : webSocket token is Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTExNzIwMTUsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI4Y2JlNjBlMi05NjI0LTQ1YmQtOWJjZS0wZjk5ZDRkNGViNmUiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.jNWNuw3EXlm8nFPnmoJEZ0aWmpxfBM9j_CI-Pw2Ds64
2020-05-30 16:00:38.175 DEBUG 5503 --- [boundChannel-43] .WebSocketAnnotationMethodMessageHandler : Searching methods to handle SEND /app/user/info session=s2bz5lbp, lookupDestination='/user/info'
2020-05-30 16:00:38.177 TRACE 5503 --- [boundChannel-43] .WebSocketAnnotationMethodMessageHandler : Found 1 handler methods: [{[/user/info],messageType=[MESSAGE]}]
2020-05-30 16:00:38.179 DEBUG 5503 --- [boundChannel-43] .WebSocketAnnotationMethodMessageHandler : Invoking com.yufei.learn.websocket.controller.SockJsSimpleController#userInfo2[1 args]
2020-05-30 16:00:38.179 TRACE 5503 --- [boundChannel-43] o.s.messaging.handler.HandlerMethod      : Arguments: [com.yufei.learn.websocket.domain.PassUser@16d40e99]
2020-05-30 16:00:38.201  INFO 5503 --- [boundChannel-43] c.y.l.w.c.SockJsSimpleController         : get user info:passUser={"id":1,"password":"elvis2020","name":"1","enabled":true,"username":"1","authorities":[],"credentials":"elvis2020","principal":"1","authenticated":true,"credentialsNonExpired":true,"accountNonExpired":true,"accountNonLocked":true}
2020-05-30 16:00:38.202 TRACE 5503 --- [boundChannel-43] o.s.m.s.u.UserDestinationMessageHandler  : No active sessions for user destination: /user/1/queue/userInfo

Comment From: xinkeng0

var headers = {
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTA2OTk0MTYsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI0NzNjOTVlYy1iNjUwLTQ2OTUtOTVhMy00ODYzZjRjOWZjOGQiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.YH6yChvdATn1vMonbc0OhSgI_kTi3KeIzgCldypInLg'
};
this.stompClient.connect({}, function (frame) {
    console.log('Connected: ' + frame)
}, headers);

Try to add authentication token when Stomp#connect.

Comment From: uyoaix

js var headers = { 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTA2OTk0MTYsInVzZXJfbmFtZSI6IjEiLCJqdGkiOiI0NzNjOTVlYy1iNjUwLTQ2OTUtOTVhMy00ODYzZjRjOWZjOGQiLCJjbGllbnRfaWQiOiJ0ZXN0LWNsaWVudC1pZCIsInNjb3BlIjpbImFsbCJdfQ.YH6yChvdATn1vMonbc0OhSgI_kTi3KeIzgCldypInLg' }; this.stompClient.connect({}, function (frame) { console.log('Connected: ' + frame) }, headers);

Try to add authentication token when Stomp#connect.

Thanks very much. I already know this method。But now I have another question: In my application, most messages are public, its no need user info. so when websocket connect, no need pass user token, just
when send user message, will pass user token in header. How to do this? when send user message, disconnect current websocket connection, and reconnect with user token? Or create a new webscoket connection just send user message, one application much websocket connection. in my word, these two ways all not suitable.