Spring WebSockets: Senden Sie Nachrichten an einen bestimmten Benutzer

1. Einleitung

In diesem Tutorial wird beschrieben, wie Sie mit Spring WebSockets STOMP-Nachrichten an einen einzelnen Benutzer senden. Das ist wichtig, weil wir manchmal nicht jede Nachricht an jeden Benutzer senden möchten. Außerdem zeigen wir Ihnen, wie Sie diese Nachrichten auf sichere Weise senden können.

In diesem großartigen Tutorial finden Sie eine Einführung in WebSockets. Weitere Informationen zur Sicherheit finden Sie in diesem Artikel, um Ihre WebSockets-Implementierung zu sichern.

2. Warteschlangen, Themen und Endpunkte

Es gibt drei Möglichkeiten, um festzustellen, wohin Nachrichten gesendet werden und wie sie mit Spring WebSockets und STOMP abonniert werden:

  1. Themen - allgemeine Konversationen oder Chat-Themen, die jedem Kunden oder Benutzer offen stehen
  2. Warteschlangen - reserviert für bestimmte Benutzer und deren aktuelle Sitzungen
  3. Endpunkte - generische Endpunkte

Lassen Sie uns nun einen kurzen Blick auf einen Beispielkontextpfad für jeden werfen:

  • "/ Topic / movies"
  • "/ User / queue / spezifischer Benutzer"
  • "/ Gesichert / Chat"

Es ist wichtig zu beachten, dass wir Warteschlangen verwenden müssen, um Nachrichten an bestimmte Benutzer zu senden, da Themen und Endpunkte diese Funktionalität nicht unterstützen .

3. Konfiguration

Lassen Sie uns nun lernen, wie Sie unsere Anwendung so konfigurieren, dass wir Nachrichten an einen bestimmten Benutzer senden können:

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(); } }

Stellen Sie sicher, dass Sie ein Benutzerziel angeben, da dies bestimmt, welche Endpunkte für einzelne Benutzer reserviert sind.

Außerdem stellen wir allen Warteschlangen und Benutzerzielen "/ gesichert" voran , damit sie authentifiziert werden müssen. Bei ungeschützten Endpunkten können wir das Präfix "/ gesichert" löschen (aufgrund unserer anderen Sicherheitseinstellungen).

Aus pom.xml- Sicht sind keine zusätzlichen Abhängigkeiten erforderlich.

4. URL-Zuordnungen

Wir möchten, dass unser Client eine Warteschlange mit einer URL-Zuordnung abonniert, die dem folgenden Muster entspricht:

"/user/queue/updates"

Diese Zuordnung wird von UserDestinationMessageHandler automatisch in die benutzersitzungsspezifische Adresse umgewandelt.

Wenn wir beispielsweise einen Benutzer mit dem Namen "user123" haben , lautet die entsprechende Adresse:

"/queue/updates-user123"

Serverseitig senden wir unsere benutzerspezifische Antwort mithilfe des folgenden URL-Zuordnungsmusters:

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

Auch dies wird in die richtige URL-Zuordnung umgewandelt, die wir bereits clientseitig abonniert haben.

Wir sehen also, dass die wesentlichen Bestandteile hier zweierlei sind:

  1. Stellen Sie unser angegebenes Benutzerzielpräfix voran (konfiguriert in AbstractWebSocketMessageBrokerConfigurer ).
  2. Verwenden Sie "/ queue" irgendwo im Mapping.

Im nächsten Abschnitt werden wir uns genau ansehen, wie das geht.

5. Aufrufen von convertAndSendToUser ()

Wir können convertAndSendToUser () nicht statisch über SimpMessagingTemplate oder SimpMessageSendingOperations aufrufen :

@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); }

Sie haben vielleicht bemerkt:

@Header("simpSessionId") String sessionId

Die Annotation @Header ermöglicht den Zugriff auf Header, die durch die eingehende Nachricht verfügbar gemacht werden . Zum Beispiel können wir die aktuelle sessionId abrufen, ohne dass komplizierte Interceptors erforderlich sind. Ebenso können wir über Principal auf den aktuellen Benutzer zugreifen .

Wichtig ist, dass der in diesem Artikel verfolgte Ansatz eine bessere Anpassung der Annotation @sendToUser in Bezug auf URL-Zuordnungen bietet . Weitere Informationen zu dieser Anmerkung finden Sie in diesem großartigen Artikel.

Auf der Clientseite verwenden wir connect () in JavaScript, um eine SockJS-Instanz zu initialisieren und mit STOMP eine Verbindung zu unserem WebSocket-Server herzustellen:

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; } 

Wir greifen auch auf die mitgelieferte Sitzungs-ID zu und hängen diese an die URL-Zuordnung " gesichert / Raum " an . Dies gibt uns die Möglichkeit, eine benutzerspezifische Abonnementwarteschlange dynamisch und manuell bereitzustellen:

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

Sobald alles eingerichtet ist, sollten wir sehen:

Und in unserer Serverkonsole:

6. Fazit

Weitere Informationen zu diesem Thema finden Sie im offiziellen Frühlingsblog und in der offiziellen Dokumentation.

Wie immer sind die in diesem Artikel verwendeten Codebeispiele auf GitHub verfügbar.