Spring WebSockets: envoyer des messages à un utilisateur spécifique

1. Introduction

Dans ce didacticiel, nous allons décrire comment utiliser Spring WebSockets pour envoyer des messages STOMP à un seul utilisateur. C'est important car nous ne voulons parfois pas diffuser chaque message à chaque utilisateur. En plus de cela, nous vous montrerons comment envoyer ces messages de manière sécurisée.

Pour une introduction aux WebSockets, consultez cet excellent didacticiel pour savoir comment être opérationnel. Et, pour une plongée plus approfondie dans la sécurité, consultez cet article pour sécuriser votre implémentation WebSockets.

2. Files d'attente, sujets et points de terminaison

Il existe trois façons principales de dire où les messages sont envoyés et comment ils sont abonnés à l' aide de Spring WebSockets et de STOMP:

  1. Sujets - conversations courantes ou sujets de discussion ouverts à tout client ou utilisateur
  2. Files d'attente - réservées à des utilisateurs spécifiques et à leurs sessions en cours
  3. Points de terminaison - points de terminaison génériques

Maintenant, jetons un coup d'œil à un exemple de chemin de contexte pour chacun:

  • "/ Sujet / films"
  • "/ User / queue / specific-user"
  • "/ Secure / chat"

Il est important de noter que nous devons utiliser des files d'attente pour envoyer des messages à des utilisateurs spécifiques, car les rubriques et les points de terminaison ne prennent pas en charge cette fonctionnalité .

3. Configuration

Maintenant, apprenons à configurer notre application afin que nous puissions envoyer des messages à un utilisateur spécifique:

public class SocketBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/secured/user/queue/specific-user"); config.setApplicationDestinationPrefixes("/spring-security-mvc-socket"); config.setUserDestinationPrefix("/secured/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/secured/room").withSockJS(); } }

Veillons à inclure une destination utilisateur car cela détermine les points de terminaison réservés aux utilisateurs uniques.

Nous préfixons également toutes nos files d'attente et destinations utilisateur par «/ secure» pour qu'elles nécessitent une authentification. Pour les terminaux non protégés, nous pouvons supprimer le préfixe «/ secure» (en raison de nos autres paramètres de sécurité).

Du point de vue pom.xml , aucune dépendance supplémentaire n'est requise.

4. Mappages d'URL

Nous voulons que notre client s'abonne à une file d'attente à l'aide d'un mappage d'URL conforme au modèle suivant:

"/user/queue/updates"

Ce mappage sera automatiquement transformé par UserDestinationMessageHandler en adresse spécifique à la session utilisateur.

Par exemple, si nous avons un utilisateur nommé «user123» , l'adresse correspondante serait:

"/queue/updates-user123"

Côté serveur, nous enverrons notre réponse spécifique à l'utilisateur en utilisant le modèle de mappage d'URL suivant:

"/user/{username}/queue/updates"

Cela sera également transformé en le mappage d'URL correct auquel nous avons déjà souscrit côté client.

Ainsi, nous voyons que les ingrédients essentiels ici sont doubles:

  1. Ajoutez notre préfixe de destination utilisateur spécifié (configuré dans AbstractWebSocketMessageBrokerConfigurer ).
  2. Utilisez «/ queue» quelque part dans le mappage.

Dans la section suivante, nous verrons exactement comment procéder.

5. Appel de convertAndSendToUser ()

Nous pouvons invoquer de manière non statique convertAndSendToUser () depuis SimpMessagingTemplate ou SimpMessageSendingOperations :

@Autowired private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/secured/room") public void sendSpecific( @Payload Message msg, Principal user, @Header("simpSessionId") String sessionId) throws Exception { OutputMessage out = new OutputMessage( msg.getFrom(), msg.getText(), new SimpleDateFormat("HH:mm").format(new Date())); simpMessagingTemplate.convertAndSendToUser( msg.getTo(), "/secured/user/queue/specific-user", out); }

Vous avez peut-être remarqué:

@Header("simpSessionId") String sessionId

L' annotation @Header permet d'accéder aux en-têtes exposés par le message entrant. Par exemple, nous pouvons récupérer le sessionId actuel sans avoir besoin d'intercepteurs compliqués. De même, nous pouvons accéder à l'utilisateur actuel via Principal .

Il est important de noter que l'approche que nous adoptons dans cet article offre une plus grande personnalisation de l' annotation @sendToUser en ce qui concerne les mappages d'URL. Pour en savoir plus sur cette annotation, consultez cet excellent article.

Côté client, nous utiliserons connect () en JavaScript pour initialiser une instance SockJS et nous connecter à notre serveur WebSocket en utilisant STOMP:

var socket = new SockJS('/secured/room'); var stompClient = Stomp.over(socket); var sessionId = ""; stompClient.connect({}, function (frame) { var url = stompClient.ws._transport.url; url = url.replace( "ws://localhost:8080/spring-security-mvc-socket/secured/room/", ""); url = url.replace("/websocket", ""); url = url.replace(/^[0-9]+\//, ""); console.log("Your current session is: " + url); sessionId = url; } 

Nous accédons également au sessionId fourni et l' ajoutons au mappage d'URL « secure / room » . Cela nous donne la possibilité de fournir dynamiquement et manuellement une file d'attente d'abonnement spécifique à l'utilisateur:

stompClient.subscribe('secured/user/queue/specific-user' + '-user' + that.sessionId, function (msgOut) { //handle messages } 

Une fois que tout est configuré, nous devrions voir:

Et dans notre console serveur:

6. Conclusion

Consultez le blog officiel de Spring et la documentation officielle pour plus d'informations sur ce sujet.

Comme toujours, les exemples de code utilisés dans cet article sont disponibles à l'adresse over sur GitHub.