Comment envoyer un message websocket du serveur à un utilisateur spécifique uniquement?
Mon application Web est configurée avec la sécurité Spring et utilise WebSocket. Je rencontre un problème délicat lorsque je tente d'envoyer un message du serveur à un utilisateur spécifique uniquement .
Ma compréhension de la lecture le manuel est du serveur que nous pouvons faire
simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);
Et du côté du client:
stompClient.subscribe('/user/reply', handler);
Mais je ne pourrais jamais obtenir le rappel d'abonnement invoqué. J'ai essayé beaucoup de chemins différents mais pas de chance.
Si je l'envoie à /topic/reply , cela fonctionne mais tous les autres utilisateurs connectés le recevront également.
Pour illustrer le problème, j'ai créé ce petit projet sur github: https://github.com/gerrytan/wsproblem
Étapes pour reproduire:
1) Clonez et construisez le projet (assurez-vous d'utiliser jdk 1.7 et maven 3.1)
$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run
2) Naviguez vers http://localhost:8080
, connectez-vous à l'aide de bob/test ou de jim/test
3) Cliquez sur "Demander un message spécifique à l'utilisateur". Attendu: un message "hello {username}" s'affiche en regard de "Message reçu à moi uniquement" pour cet utilisateur uniquement. Réel: rien n'est reçu.
Oh, client side no need to known about current user
, Le serveur le fera pour vous.
Côté serveur, utilisez la méthode suivante pour envoyer un message à un utilisateur:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Remarque: en utilisant queue
, pas topic
, Spring toujours en utilisant queue
avec sendToUser
Côté client
stompClient.subscribe("/user/queue/reply", handler);
Expliquez
Lorsqu'une connexion websocket est ouverte, Spring lui attribue un session id
(Et non HttpSession
, attribuez par connexion). Et lorsque votre client s'abonne à un canal, démarrez par /user/
, Par exemple: /user/queue/reply
, Votre instance de serveur s'abonnera à une file d'attente nommée queue/reply-user[session id]
Lorsque vous utilisez envoyer un message à l'utilisateur, par exemple: nom d'utilisateur est admin
vous écrirez simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
Spring déterminera quel session id
Est mappé à l'utilisateur admin
. Ex: il a trouvé deux sessions wsxedc123
Et thnujm456
, Spring le traduira en 2 destinations queue/reply-userwsxedc123
Et queue/reply-userthnujm456
, Et enverra votre message avec 2 destinations à votre courtier de messages.
Le courtier de messages reçoit les messages et les renvoie à votre instance de serveur qui conserve la session correspondant à chaque session (les sessions WebSocket peuvent être conservées par un ou plusieurs serveurs). Spring traduira le message en destination
(par exemple: user/queue/reply
) Et en session id
(Par exemple: wsxedc123
). Ensuite, il envoie le message au Websocket session
Correspondant.
Ah j'ai trouvé quel était mon problème. D'abord, je n'ai pas enregistré le préfixe /user
Sur le courtier simple
<websocket:simple-broker prefix="/topic,/user" />
Alors je n'ai pas besoin du préfixe supplémentaire /user
Pour envoyer:
convertAndSendToUser(principal.getName(), "/reply", reply);
Spring ajoutera automatiquement "/user/" + principal.getName()
à la destination, d'où sa résolution en "/ user/bob/reply".
Cela signifie également qu'en javascript, je devais m'abonner à une adresse différente par utilisateur.
stompClient.subscribe('/user/' + userName + '/reply,...)
Exactement j'ai fait la même chose et cela fonctionne sans utiliser l'utilisateur
@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");
}
}
Ma solution basée sur la meilleure explication de Thanh Nguyen Van, mais en plus, j'ai configuré MessageBrokerRegistry:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/", "/topic/");
...
}
...
}
J'ai également créé un exemple de projet Websocket à l'aide de STOMP. Ce que je remarque, c'est 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();
}
}
cela fonctionne que "/ user" soit inclus ou non dans config.enableSimpleBroker (...