web-dev-qa-db-fra.com

D'où vient "utilisateur" dans convertAndSendToUser fonctionne dans SockJS + Spring Websocket?

J'aimerais comprendre le fonctionnement de convertAndSendToUser dans le cadre Spring SockJS + Websocket.

Dans le client, nous nous connecterions en tant que

stompClient.connect(login, password, callback())

qui se traduira par une demande de connexion avec "informations d'identification Stomp" de login et mot de passe, qui peut être vu par exemple. si nous gérons SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/

Mais je ne sais toujours pas s'il s'agira de "l'utilisateur" dans une opération d'envoi côté serveur vers une file d'attente:

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

Le plus proche que je puisse obtenir est de lire ce fil Envoi d’un message à un utilisateur spécifique sur Spring Websocket , réponse de Thanh Nguyen Van, mais cela n’est pas encore clair.

Fondamentalement, ce que je dois faire est de souscrire certains clients au même sujet, mais sur un serveur, leur envoyer des données différentes. Le client peut fournir l'identifiant de l'utilisateur.

15
Askar Ibragimov

J'ai essayé de comprendre les websockets et suis tombé sur cette question. Je suis vraiment déçu de ne pas trouver de réponse. Voici quelque chose pour le futur lecteur.

Je suppose que le lecteur a une compréhension de base des Websockets à ressort utilisant Stomp. Les termes tels que abonnement, préfixes de destination, sujets, fichier de configuration de socket, etc. sont compris.

Nous savons que nous pouvons envoyer des messages au client à partir d'un serveur Stomp en utilisant les préfixes de rubrique auxquels il est abonné, par exemple. /topic/hello. Nous savons également que nous pouvons envoyer des messages à un utilisateur spécifique car spring fournit l’API convertAndSendToUser(username, destination, message). Il accepte un nom d'utilisateur String, ce qui signifie que si nous avons en quelque sorte un nom d'utilisateur unique pour chaque connexion, nous devrions pouvoir envoyer des messages à des utilisateurs spécifiques abonnés à un sujet.

Ce qui est moins compris c'est d'où vient ce nom d'utilisateur?

Ce nom d'utilisateur fait partie d'une interface Java.security.Principal. Chaque objet StompHeaderAccessor ou WebSocketSession a une instance de ce principal et vous pouvez obtenir le nom d'utilisateur. Cependant, selon mes expériences, il n'est pas généré automatiquement. Il doit être généré manuellement par le serveur pour chaque session.  

Pour utiliser cette interface, vous devez d'abord la mettre en œuvre.

class StompPrincipal implements Principal {
    String name

    StompPrincipal(String name) {
        this.name = name
    }

    @Override
    String getName() {
        return name
    }
}

Ensuite, vous pouvez générer une StompPrincipal unique pour chaque connexion en remplaçant le DefaultHandshakeHandler. Vous pouvez utiliser n'importe quelle logique pour générer le nom d'utilisateur. Voici une logique potentielle qui utilise UUID:

class CustomHandshakeHandler extends DefaultHandshakeHandler {
    // Custom class for storing principal
    @Override
    protected Principal determineUser(ServerHttpRequest request,
                                      WebSocketHandler wsHandler,
                                      Map<String, Object> attributes) {
        // Generate principal with UUID as name
        return new StompPrincipal(UUID.randomUUID().toString())
    }
}

Enfin, vous devez configurer vos websockets pour utiliser votre gestionnaire de prise de contact personnalisé.

@Override
void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
    stompEndpointRegistry
         .addEndpoint("/stomp") // Set websocket endpoint to connect to
         .setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler
         .withSockJS() // Add Sock JS support
}

C'est tout. Votre serveur est maintenant configuré pour générer un nom de principal unique pour chaque connexion. Il transmettra ce principal dans le cadre des objets StomHeaderAccessor auxquels vous pouvez accéder via les écouteurs d'événements de connexion, les fonctions MessageMapping, etc.

Des auditeurs de l'événement:

@EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
    // Get Accessor
    StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage())
}

À partir des API mappées de messages

@MessageMapping('/hello')
protected void hello(SimpMessageHeaderAccessor sha, Map message) {
    // sha available in params
}

Une dernière remarque sur l'utilisation de convertAndSendToUser(...). Lors de l'envoi de messages à un utilisateur, vous utiliserez quelque chose comme ceci 

convertAndSendToUser(sha.session.principal.name, '/topic/hello', message)

Cependant, pour abonner le client, vous utiliserez

client.subscribe('/user/topic/hello', callback)

Si vous abonnez le client à /topic/hello, vous ne recevrez que les messages diffusés.

21
Siddharth Garg

Je n'ai pas fait de configuration spécifique et je peux simplement faire ceci:

@MessageMapping('/hello')
protected void hello(Principal principal, Map message) {
    String username = principal.getName();
}
0
Wenneguen