Come inviare un messaggio WebSocket dal server solo a un utente specifico?
La mia webapp ha una configurazione di sicurezza primaverile e utilizza websocket. Sto incontrando problemi complessi cercando di inviare messaggi dal server solo a un utente specifico .
La mia comprensione dalla lettura del manuale proviene dal server che possiamo fare
simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);
E sul lato client:
stompClient.subscribe('/user/reply', handler);
Ma non ho mai potuto richiamare la richiamata della sottoscrizione. Ho provato molti percorsi diversi ma senza fortuna.
Se lo invio a / topic / reply funziona, ma anche tutti gli altri utenti connessi lo riceveranno.
Per illustrare il problema ho creato questo piccolo progetto su github: https://github.com/gerrytan/wsproblem
Passi per riprodurre:
1) Clona e crea il progetto (assicurati di utilizzare jdk 1.7 e maven 3.1)
$ git clone https://github.com/gerrytan/wsproblem.git $ cd wsproblem $ mvn jetty:run
2) Passare a http://localhost:8080
, accedere utilizzando bob / test o jim / test
3) Fare clic su “Richiedi msg specifico dell’utente”. Previsto: un messaggio “ciao {nome utente}” viene visualizzato accanto a “Solo messaggio ricevuto per me” solo per questo utente, Effettivo: non viene ricevuto nulla
Oh, client side no need to known about current user
, il server lo farà per te.
Sul lato server, utilizzando il seguente modo per inviare un messaggio a un utente:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Nota: utilizzando la queue
, non l’ topic
, Spring utilizza sempre la queue
con sendToUser
Dal lato del cliente
stompClient.subscribe("/user/queue/reply", handler);
Spiegare
Quando viene aperta una qualsiasi connessione WebSocket, Spring assegna un session id
(non HttpSession
, assegnato per connessione). E quando il tuo cliente si iscrive a un canale inizia con /user/
, ad esempio: /user/queue/reply
, l’istanza del server si abbonerà a una coda chiamata queue/reply-user[session id]
Quando usi invia un messaggio all’utente, ad esempio: username è admin
Scriverete simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
Spring determinerà quale session id
mappato all’amministratore dell’utente. Ad es .: Ha trovato due sessioni wsxedc123
e thnujm456
, Spring la tradurrà in 2 queue/reply-userwsxedc123
destinazione queue/reply-userwsxedc123
e in queue/reply-userthnujm456
, e invierà il tuo messaggio con 2 destinazioni al tuo broker di messaggi.
Il broker dei messaggi riceve i messaggi e li restituisce all’istanza del server che tiene la sessione corrispondente a ciascuna sessione (le sessioni WebSocket possono essere conservate da uno o più server). Spring tradurrà il messaggio a destination
(es .: user/queue/reply
) e session id
(es .: wsxedc123
). Quindi, invia il messaggio alla Websocket session
corrispondente
Ah ho trovato qual era il mio problema. Per prima cosa non ho registrato il prefisso /user
sul broker semplice
Quindi non ho bisogno del prefisso extra /user
quando invio:
convertAndSendToUser(principal.getName(), "/reply", reply);
Spring aggiungerà automaticamente "/user/" + principal.getName()
alla destinazione, quindi si risolverà in “/ user / bob / reply”.
Ciò significa anche in javascript che dovevo iscriversi a diversi indirizzi per utente
stompClient.subscribe('/user/' + userName + '/reply,...)
La mia soluzione è basata sulla migliore spiegazione di Thanh Nguyen Van, ma in aggiunta ho configurato MessageBrokerRegistry:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/queue/", "/topic/"); ... } ... }
Ho creato anche un progetto websocket di esempio con STOMP. Quello che sto notando è quello
@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(); }
}
funziona anche se “/ user” è incluso in config.enableSimpleBroker (…
Esattamente ho fatto lo stesso e funziona senza usare l’utente
@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"); } }