web-dev-qa-db-fra.com

Envoi d'un message à un utilisateur spécifique sur Spring Websocket

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.

67
gerrytan

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.

64
Thanh Nguyen Van

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,...) 
28
gerrytan

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");
    }
}
2
Sid

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/");
        ...
    }
    ...
}
2
Nikolay Shabak

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 (...

2
Kans