Перейти к основному содержимому

Spring WebSockets: отправка сообщений конкретному пользователю

· 4 мин. чтения

1. Введение

В этом руководстве мы опишем, как использовать Spring WebSockets для отправки сообщений STOMP одному пользователю. Это важно, потому что иногда мы не хотим транслировать каждое сообщение каждому пользователю. Кроме того, мы покажем, как отправлять эти сообщения безопасным способом.

Для ознакомления с WebSockets ознакомьтесь с этим замечательным руководством о том, как приступить к работе. И, чтобы глубже погрузиться в безопасность, ознакомьтесь с этой статьей, чтобы защитить свою реализацию WebSockets.

2. Очереди, темы и конечные точки

Есть три основных способа сказать, куда отправляются сообщения и как они подписываются, используя Spring WebSockets и STOMP:

  1. Темы — общие разговоры или темы чата, открытые для любого клиента или пользователя.
  2. Очереди — зарезервированы для конкретных пользователей и их текущих сеансов.
  3. Конечные точки — общие конечные точки

Теперь давайте быстро рассмотрим пример контекстного пути для каждого:

  • «/тема/фильмы»
  • «/пользователь/очередь/конкретный-пользователь»
  • «/защищенный/чат»

Важно отметить, что мы должны использовать очереди для отправки сообщений определенным пользователям, поскольку темы и конечные точки не поддерживают эту функцию .

3. Конфигурация

Теперь давайте узнаем, как настроить наше приложение, чтобы мы могли отправлять сообщения конкретному пользователю:

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

Давайте обязательно включим пункт назначения пользователя, так как он определяет, какие конечные точки зарезервированы для отдельных пользователей.

Мы также добавляем префикс «/secured» ко всем нашим очередям и адресатам пользователей , чтобы они требовали аутентификации. Для незащищенных конечных точек мы можем отказаться от префикса «/secured» (из-за других наших настроек безопасности).

С точки зрения pom.xml никаких дополнительных зависимостей не требуется.

4. Сопоставление URL-адресов

Мы хотим, чтобы наш клиент подписался на очередь, используя сопоставление URL-адресов, соответствующее следующему шаблону:

"/user/queue/updates"

Это сопоставление будет автоматически преобразовано UserDestinationMessageHandler в адрес, специфичный для сеанса пользователя.

Например, если у нас есть пользователь с именем «user123» , соответствующий адрес будет таким:

"/queue/updates-user123"

На стороне сервера мы отправим наш пользовательский ответ, используя следующий шаблон сопоставления URL-адресов:

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

Это также будет преобразовано в правильное сопоставление URL-адресов, на которое мы уже подписались на стороне клиента.

Таким образом, мы видим, что основные ингредиенты здесь двояки:

  1. Добавьте наш указанный префикс назначения пользователя (настроенный в AbstractWebSocketMessageBrokerConfigurer ).
  2. Используйте «/queue» где-нибудь в сопоставлении.

В следующем разделе мы рассмотрим, как именно это сделать.

5. Вызов convertAndSendToUser()

Мы можем не статически вызывать convertAndSendToUser() из SimpMessagingTemplate или 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);
}

Вы могли заметить:

@Header("simpSessionId") String sessionId

Аннотация @Header позволяет получить доступ к заголовкам, предоставляемым входящим сообщением. Например, мы можем получить текущий идентификатор сеанса без необходимости использования сложных перехватчиков. Точно так же мы можем получить доступ к текущему пользователю через Principal .

Важно отметить, что подход, который мы используем в этой статье, обеспечивает более широкие возможности настройки по сравнению с аннотацией @sendToUser в отношении сопоставлений URL-адресов. Чтобы узнать больше об этой аннотации, ознакомьтесь с этой замечательной статьей.

На стороне клиента мы будем использовать connect() в JavaScript для инициализации экземпляра SockJS и подключения к нашему серверу WebSocket с помощью 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;
}

Мы также получаем доступ к предоставленному идентификатору сеанса и добавляем его к сопоставлению URL -адреса « secure/room » . Это дает нам возможность динамически и вручную предоставлять очередь подписки для конкретного пользователя:

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

После того, как все настроено, мы должны увидеть:

./fa78c87d15daaca598b9e7170b64c059.png

И в консоли нашего сервера:

./41dd096211c470ee5aa9b6f828f6cb0a.png

6. Заключение

Посетите официальный блог Spring и официальную документацию для получения дополнительной информации по этой теме.

Как всегда, образцы кода, использованные в этой статье, доступны на GitHub .