Рубрики
Без рубрики

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

Узнайте, как отправлять сообщения STOMP конкретному пользователю в Spring WebSockets.

Автор оригинала: baeldung.

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

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

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

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

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

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

"/user/queue/updates"

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

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

"/queue/updates-user123"

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

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

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

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

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

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

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 позволяет получить доступ к заголовкам, отображаемым входящим сообщением. Например, мы можем захватить текущий идентификатор сеанса без необходимости в сложных перехватчиках. Аналогично, мы можем получить доступ к текущему пользователю через Принципал .

Важно отметить, что подход, который мы используем в этой статье, обеспечивает большую настройку по сравнению с аннотацией @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-адресов. Это дает нам возможность динамически и вручную предоставлять пользовательскую очередь подписки:

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

Как только все будет готово, мы должны посмотреть:

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

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

Ознакомьтесь с официальным Spring blog и официальной документацией для получения дополнительной информации по этой теме.

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