Wie sende ich eine Websocket-Nachricht vom Server nur an einen bestimmten Benutzer?
Meine Webapp hat Spring Security Setup und verwendet Websocket. Beim Versuch, eine Nachricht vom Server nur an einen bestimmten Benutzer zu senden, tritt ein schwieriges Problem auf .
Mein Verständnis vom Lesen des Handbuchs stammt von dem Server, den wir ausführen können
simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);
Und auf der Kundenseite:
stompClient.subscribe('/user/reply', handler);
Aber ich konnte den Abonnement-Rückruf niemals aufrufen. Ich habe viele verschiedene Wege ausprobiert, aber kein Glück.
Wenn ich es an / topic / reply sende, funktioniert es, aber alle anderen verbundenen Benutzer erhalten es auch.
Um das Problem zu veranschaulichen, habe ich dieses kleine Projekt auf Github erstellt: https://github.com/gerrytan/wsproblem
Schritte zum Reproduzieren:
1) Klonen und erstellen Sie das Projekt (stellen Sie sicher, dass Sie jdk 1.7 und maven 3.1 verwenden).
$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run
2) Navigieren Sie zu http://localhost:8080
und melden Sie sich entweder mit Bob / Test oder Jim / Test an
3) Klicken Sie auf "Benutzerspezifische Nachricht anfordern". Erwartet: Eine Meldung "Hallo {Benutzername}" wird neben "Nur für mich empfangene Nachricht" nur für diesen Benutzer angezeigt. Tatsächlich: Es wird nichts empfangen
quelle
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);
und wenn die Nachricht vom Server gesendet wird,java.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
convertAndSendToUser(principal.getName(), "/reply", reply);
Antworten:
Oh, der
client side no need to known about current user
Server wird das für Sie tun.Auf der Serverseite können Sie auf folgende Weise Nachrichten an einen Benutzer senden:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Hinweis: Die Verwendung
queue
, nichttopic
, Frühling immer mitqueue
mitsendToUser
Auf der Client-Seite
stompClient.subscribe("/user/queue/reply", handler);
Erklären
Wenn eine Websocket-Verbindung geöffnet ist, weist Spring ihr eine zu
session id
(nichtHttpSession
pro Verbindung zuweisen). Wenn Ihr Client einen Kanal abonniert, beginnen Sie beispielsweise mit/user/
:/user/queue/reply
Ihre Serverinstanz abonniert eine Warteschlange mit dem Namenqueue/reply-user[session id]
Wenn Sie Nachricht an Benutzer senden verwenden, z. B.: Benutzername ist
admin
Sie werden schreibensimpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
Die Feder bestimmt, welche
session id
dem Benutzer zugeordnet istadmin
. Beispiel: Es wurden zwei Sitzungen gefunden,wsxedc123
undthnujm456
Spring übersetzt es in zwei Zielequeue/reply-userwsxedc123
undqueue/reply-userthnujm456
sendet Ihre Nachricht mit zwei Zielen an Ihren Nachrichtenbroker.Der Nachrichtenbroker empfängt die Nachrichten und gibt sie an Ihre Serverinstanz zurück, die die Sitzung enthält, die jeder Sitzung entspricht (WebSocket-Sitzungen können von einem oder mehreren Servern gehalten werden). Spring übersetzt die Nachricht in
destination
(zB :)user/queue/reply
undsession id
(zB :)wsxedc123
. Dann sendet es die Nachricht an die entsprechendeWebsocket session
quelle
HttpSession
als eine Websocket-Verbindung hergestellt wurdeDefaultHandshakeHandler
Methode erweitern und überschreibendetermineUser
queue/reply-user[session id]
Teil im offiziellen Dokument?Ah, ich habe herausgefunden, was mein Problem war. Zuerst habe ich das
/user
Präfix nicht beim einfachen Broker registriert<websocket:simple-broker prefix="/topic,/user" />
Dann brauche ich
/user
beim Senden kein zusätzliches Präfix:convertAndSendToUser(principal.getName(), "/reply", reply);
Spring wird
"/user/" + principal.getName()
dem Ziel automatisch vorangestellt und daher in "/ user / bob / reply" aufgelöst.Dies bedeutet auch, dass ich in Javascript eine andere Adresse pro Benutzer abonnieren musste
stompClient.subscribe('/user/' + userName + '/reply,...)
quelle
@RequestMapping
und@MessageMapping
siehe auch hier ? Wie abonniere ich mithilfe der Sping Websocket-Integration für einen bestimmten Benutzernamen (Benutzer-ID) + erhalte Benachrichtigungen von Methoden, die mit @RequestMapping versehen sind?Ich habe auch ein Beispiel-Websocket-Projekt mit STOMP erstellt. Was ich bemerke, ist das
@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(); }
}}
Es funktioniert unabhängig davon, ob "/ user" in config.enableSimpleBroker enthalten ist (...
quelle
Meine Lösung basiert auf der besten Erklärung von Thanh Nguyen Van, aber zusätzlich habe ich MessageBrokerRegistry konfiguriert:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/queue/", "/topic/"); ... } ... }
quelle
Genau das habe ich auch getan und es funktioniert ohne Benutzer
@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"); } }
quelle
/app
? In welchem Fall?