Spring WebSockets: создать чат пользователя

Spring WebSockets: создать чат пользователя

1. Вступление

В этом руководстве мы расскажем, какuse Spring WebSockets to send STOMP messages to a single user.. Это важно, потому что иногда мы не хотим транслировать каждое сообщение каждому пользователю. Кроме того, мы продемонстрируем, как безопасно отправлять эти сообщения.

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

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

three main ways to say where messages are sent and how they are subscribed to используют Spring WebSockets и STOMP:

  1. Topics - общие разговоры или темы чата, открытые для любого клиента или пользователя

  2. Queues - зарезервировано для определенных пользователей и их текущих сеансов

  3. Endpoints - общие конечные точки

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

  • “/topic/movies”

  • “/user/queue/specific-user”

  • “/secured/chat”

Важно отметить, что we must use queues to send messages to specific users, as topics and endpoints don’t support this functionality.

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, которое мы уже подписали на стороне клиента.

Таким образом, мы видим, чтоthe essential ingredients here are two-fold:

  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

The @Header annotation allows access to headers exposed by the inbound message. Например, мы можем получить текущийsessionId без необходимости в сложных перехватчиках. Аналогичноwe can access the current user via Principal.

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

На стороне клиента мы будем использоватьconnect() в JavaScript доinitialize 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;
}

Мы также получаем доступ к предоставленномуsessionId и добавляем его к сопоставлению 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
}

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

 

image

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

image

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

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

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