Spring WebSocket: invia messaggi a un utente specifico

1. Introduzione

In questo tutorial, descriveremo come utilizzare Spring WebSocket per inviare messaggi STOMP a un singolo utente. Questo è importante perché a volte non vogliamo trasmettere ogni messaggio a tutti gli utenti. Oltre a ciò, mostreremo come inviare questi messaggi in modo sicuro.

Per un'introduzione a WebSocket, dai un'occhiata a questo fantastico tutorial su come iniziare a lavorare. E, per un'analisi più approfondita della sicurezza, consulta questo articolo per proteggere la tua implementazione WebSocket.

2. Code, argomenti ed endpoint

Esistono tre modi principali per stabilire dove vengono inviati i messaggi e come vengono sottoscritti utilizzando Spring WebSocket e STOMP:

  1. Argomenti : conversazioni comuni o argomenti di chat aperti a qualsiasi cliente o utente
  2. Code : riservate a utenti specifici e alle loro sessioni correnti
  3. Endpoint : endpoint generici

Ora diamo una rapida occhiata a un percorso di contesto di esempio per ciascuno:

  • "/ Topic / movies"
  • "/ User / queue / specific-user"
  • "/ Secured / chat"

È importante notare che dobbiamo utilizzare le code per inviare messaggi a utenti specifici, poiché gli argomenti e gli endpoint non supportano questa funzionalità .

3. Configurazione

Ora, impariamo come configurare la nostra applicazione in modo da poter inviare messaggi a un utente specifico:

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

Assicuriamoci di includere una destinazione utente poiché ciò determina quali endpoint sono riservati ai singoli utenti.

Inoltre, anteponiamo a tutte le nostre code e destinazioni utente "/ secured" per richiedere l'autenticazione. Per gli endpoint non protetti, possiamo eliminare il prefisso "/ secured" (come risultato delle nostre altre impostazioni di sicurezza).

Da un punto di vista pom.xml , non sono richieste dipendenze aggiuntive.

4. Mappature URL

Vogliamo che il nostro client si iscriva a una coda utilizzando una mappatura URL conforme al seguente modello:

"/user/queue/updates"

Questa mappatura verrà trasformata automaticamente da UserDestinationMessageHandler nell'indirizzo specifico della sessione dell'utente.

Ad esempio, se abbiamo un utente denominato "utente123" , l'indirizzo corrispondente sarebbe:

"/queue/updates-user123"

Lato server, invieremo la nostra risposta specifica dell'utente utilizzando il seguente pattern di mappatura URL:

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

Anche questo verrà trasformato nella mappatura URL corretta che abbiamo già sottoscritto sul lato client.

Quindi, vediamo che gli ingredienti essenziali qui sono duplici:

  1. Anteponi il nostro prefisso di destinazione utente specificato (configurato in AbstractWebSocketMessageBrokerConfigurer ).
  2. Usa "/ queue" da qualche parte all'interno della mappatura.

Nella prossima sezione, daremo uno sguardo esattamente a come farlo.

5. Invocare convertAndSendToUser ()

Possiamo richiamare convertAndSendToUser () in modo non statico da SimpMessagingTemplate o 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); }

Potresti aver notato:

@Header("simpSessionId") String sessionId

L' annotazione @Header consente l'accesso alle intestazioni esposte dal messaggio in entrata. Ad esempio, possiamo acquisire l'attuale sessionId senza la necessità di complicati intercettori. Allo stesso modo, possiamo accedere all'utente corrente tramite Principal .

È importante sottolineare che l'approccio che adottiamo in questo articolo fornisce una maggiore personalizzazione rispetto all'annotazione @sendToUser rispetto alle mappature URL. Per ulteriori informazioni su questa annotazione, dai un'occhiata a questo fantastico articolo.

Sul lato client, useremo connect () in JavaScript per inizializzare un'istanza SockJS e connetterci al nostro server WebSocket utilizzando 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; } 

Accediamo anche al sessionId fornito e lo aggiungiamo alla mappatura URL " protetta / stanza " . Questo ci dà la possibilità di fornire dinamicamente e manualmente una coda di sottoscrizione specifica per l'utente:

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

Una volta che tutto è impostato, dovremmo vedere:

E nella nostra console del server:

6. Conclusione

Controlla il blog ufficiale di Spring e la documentazione ufficiale per ulteriori informazioni su questo argomento.

Come sempre, gli esempi di codice utilizzati in questo articolo sono disponibili su GitHub.