Spring WebSockets: criar um bate-papo de usuário
1. Introdução
Neste tutorial, descreveremos comouse Spring WebSockets to send STOMP messages to a single user. Isso é importante porque às vezes não queremos transmitir todas as mensagens para todos os usuários. Além disso, vamos demonstrar como enviar essas mensagens de forma segura.
2. Filas, tópicos e endpoints
Existemthree main ways to say where messages are sent and how they are subscribed to usando Spring WebSockets e STOMP:
-
Topics - conversas comuns ou tópicos de chat abertos para qualquer cliente ou usuário
-
Queues - reservado para usuários específicos e suas sessões atuais
-
Endpoints - endpoints genéricos
Agora, vamos dar uma olhada rápida em um exemplo de caminho de contexto para cada:
-
“/topic/movies”
-
“/user/queue/specific-user”
-
“/secured/chat”
É importante observar que we must use queues to send messages to specific users, as topics and endpoints don’t support this functionality.
3. Configuração
Agora, vamos aprender como configurar nosso aplicativo para que possamos enviar mensagens a um usuário específico:
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();
}
}
Vamos nos certificar de incluir um destino de usuário, pois isso determina quais terminais são reservados para usuários únicos.
Também prefixamos todas as nossas filas e destinos de usuário com“/secured” para que exijam autenticação. Para endpoints desprotegidos, podemos descartar o prefixo“/secured” (como resultado de nossas outras configurações de segurança).
Do ponto de vista depom.xml, nenhuma dependência adicional é necessária.
4. Mapeamentos de URL
Queremos que nosso cliente assine uma fila usando um mapeamento de URL que esteja em conformidade com o seguinte padrão:
"/user/queue/updates"
Esse mapeamento será transformado automaticamente porUserDestinationMessageHandler no endereço específico da sessão do usuário.
Por exemplo, se tivermos um usuário chamado“user123”, o endereço correspondente seria:
"/queue/updates-user123"
Do lado do servidor, enviaremos nossa resposta específica do usuário usando o seguinte padrão de mapeamento de URL:
"/user/{username}/queue/updates"
Isso também será transformado no mapeamento de URL correto que já assinamos no lado do cliente.
Assim, vemos quethe essential ingredients here are two-fold:
-
Prefixe nosso Prefixo de destino do usuário especificado (configurado emAbstractWebSocketMessageBrokerConfigurer).
-
Use“/queue” em algum lugar dentro do mapeamento.
Na próxima seção, veremos exatamente como fazer isso.
5. InvocandoconvertAndSendToUser()
Podemos invocar não estaticamenteconvertAndSendToUser() deSimpMessagingTemplate ouSimpMessageSendingOperations:
@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);
}
Você deve ter notado:
@Header("simpSessionId") String sessionId
The @Header annotation allows access to headers exposed by the inbound message. Por exemplo, podemos pegar osessionId atual sem a necessidade de interceptores complicados. Da mesma forma,we can access the current user via Principal.
É importante ressaltar que a abordagem que adotamos neste artigo fornece maior personalização em relação à anotação@sendToUser com relação aos mapeamentos de URL. Para obter mais informações sobre essa anotação, verifique o excelente artigo dethis.
Do lado do cliente, usaremosconnect() em JavaScript parainitialize a SockJS instance and connect to our WebSocket server using 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;
}
Também acessamos osessionId fornecido e o anexamos ao mapeamento de URL “secured/room“ . This gives us the ability to dynamically and manually supply a user-specific subscription queue:
stompClient.subscribe('secured/user/queue/specific-user'
+ '-user' + that.sessionId, function (msgOut) {
//handle messages
}
Depois que tudo estiver configurado, devemos ver:
E no nosso console do servidor:
6. Conclusão
Verifique o Springblog oficial eofficial documentation para obter mais informações sobre este tópico.
Como sempre, os exemplos de código usados neste artigo estão disponíveisover on GitHub.