Enviar mensaje a un usuario específico en Spring Websocket

¿Cómo enviar un mensaje de websocket del servidor a un usuario específico solamente?

Mi aplicación web tiene configuración de seguridad de spring y usa websocket. Me encuentro con un problema complicado tratando de enviar mensajes del servidor a usuarios específicos solamente .

Mi comprensión al leer el manual es del servidor que podemos hacer

simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply); 

Y en el lado del cliente:

 stompClient.subscribe('/user/reply', handler); 

Pero nunca podría obtener la callback de suscripción invocada. He intentado muchos caminos diferentes, pero no tuve suerte.

Si lo envío a / topic / reply funciona, pero todos los demás usuarios conectados también lo recibirán.

Para ilustrar el problema, he creado este pequeño proyecto en github: https://github.com/gerrytan/wsproblem

Pasos para reproducir:

1) Clona y crea el proyecto (asegúrate de estar usando jdk 1.7 y maven 3.1)

 $ git clone https://github.com/gerrytan/wsproblem.git $ cd wsproblem $ mvn jetty:run 

2) Navegue a http://localhost:8080 , inicie sesión usando bob / test o jim / test

3) Haga clic en “Solicitar msg específico del usuario”. Esperado: aparece un mensaje “hola {nombre de usuario}” junto a “Mensaje recibido solo para mí” para este usuario, real: no se recibe nada

    Oh, client side no need to known about current user , el servidor lo hará por usted.

    Del lado del servidor, usando la siguiente forma para enviar un mensaje a un usuario:

     simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message); 

    Nota: Usando queue , no topic , Spring siempre usando queue con sendToUser

    Del lado del cliente

     stompClient.subscribe("/user/queue/reply", handler); 

    Explique

    Cuando cualquier conexión de websocket está abierta, Spring le asignará una session id (no HttpSession , asignar por conexión). Y cuando su cliente se suscribe a un canal, comience con /user/ , por ejemplo: /user/queue/reply , su instancia de servidor se suscribirá a una cola llamada queue/reply-user[session id]

    Cuando use enviar mensaje al usuario, por ejemplo: username es admin Escribirá simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

    Spring determinará qué session id asignó al admin usuario. Por ejemplo: encontró dos sesiones wsxedc123 y thnujm456 , Spring la traducirá a 2 destinos queue/reply-userwsxedc123 y queue/reply-userthnujm456 , y enviará su mensaje con 2 destinos a su intermediario de mensajes.

    El intermediario de mensajes recibe los mensajes y los devuelve a su instancia de servidor que contiene la sesión correspondiente a cada sesión (las sesiones de WebSocket pueden ser retenidas por uno o más servidores). Spring traducirá el mensaje a destination (p. Ej .: user/queue/reply ) y la session id (p. wsxedc123 .: wsxedc123 ). Luego, envía el mensaje a la Websocket session correspondiente de Websocket session

    Ah, encontré cuál era mi problema. Primero, no registré el prefijo /user en el simple broker

      

    Entonces no necesito el prefijo extra /user al enviar:

     convertAndSendToUser(principal.getName(), "/reply", reply); 

    Spring agregará automáticamente "/user/" + principal.getName() al destino, por lo que se convierte en “/ user / bob / reply”.

    Esto también significa que en javascript tuve que suscribirme a diferentes direcciones por usuario

     stompClient.subscribe('/user/' + userName + '/reply,...) 

    Mi solución está basada en la mejor explicación de Thanh Nguyen Van, pero además he configurado MessageBrokerRegistry:

     @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/queue/", "/topic/"); ... } ... } 

    Creé un proyecto de websocket de muestra usando STOMP también. Lo que noto es que

     @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic", "/queue");// including /user also works config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/getfeeds").withSockJS(); } 

    }

    funciona si se incluye o no “/ user” en config.enableSimpleBroker (…

    Exactamente hice lo mismo y está funcionando sin usar usuario

     @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic" , "/queue"); config.setApplicationDestinationPrefixes("/app"); } }