Откуда "пользователь" приходит в convertAndSendToUser работает в SockJS+Spring Websocket?

Я хотел бы понять, как convertAndSendToUser работает в Spring SockJS+Websocket framework.

в клиенте, мы соединялись бы как

stompClient.connect(login, password, callback())

что приведет к запросу подключения с "учетными данными Stomp" логина и пароля, которые можно увидеть, например, если мы обрабатываем SessionConnectEvent http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/

но мне остается неясным, будет ли это "пользователь" означает на стороне сервера операцию отправки в очередь:

 simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);

самое близкое, что я могу получить, это прочитать эту тему отправка сообщения конкретному пользователю на Spring Websocket, ответ Тхань Нгуен Ван, но это все еще неясно.

в основном, что мне нужно сделать, это подписаться на некоторые клиенты на ту же тему, но на сервере, отправить им разные данные. Клиент может предоставить идентификатор пользователя.

2 ответов


я пытался понять websockets и наткнулся на этот вопрос. Я был очень разочарован, не найдя ответа. Вот кое-что для будущего читателя.

я предположу, что у читателя есть базовое понимание весенних websockets с использованием Stomp. Такие термины, как подписка, префиксы назначения, темы, файл конфигурации сокета и т. д. суть понял.

мы знаем, что можем отправлять сообщения клиенту с сервера stomp, используя префиксы темы, которые он подписаны, например,/topic/hello. Мы также знаем, что можем отправлять сообщения конкретному пользователю, потому что spring предоставляет convertAndSendToUser(username, destination, message) API-интерфейс. Он принимает строковое имя пользователя, что означает, что если у нас есть уникальное имя пользователя для каждого соединения, мы должны иметь возможность отправлять сообщения определенным пользователям, подписанным на тему.

что менее понятно, откуда взялось это имя пользователя ?

это имя пользователя является частью java.security.Principal интерфейс. каждый StompHeaderAccessor или WebSocketSession объект имеет экземпляр этого участника, и вы можете получить от него имя пользователя. Однако, согласно моим экспериментам, он не создается автоматически. он должен генерироваться сервером вручную для каждого сеанса.

чтобы использовать этот интерфейс, сначала вам нужно его реализовать.

class StompPrincipal implements Principal {
    String name

    StompPrincipal(String name) {
        this.name = name
    }

    @Override
    String getName() {
        return name
    }
}

затем вы можете создать уникальный StompPrincipal для каждого соединения путем переопределения DefaultHandshakeHandler. Вы можете использовать любую логику для создания имя пользователя:. Вот одна потенциальная логика, которая использует UUID:

class CustomHandshakeHandler extends DefaultHandshakeHandler {
    // Custom class for storing principal
    @Override
    protected Principal determineUser(ServerHttpRequest request,
                                      WebSocketHandler wsHandler,
                                      Map<String, Object> attributes) {
        // Generate principal with UUID as name
        return new StompPrincipal(UUID.randomUUID().toString())
    }
}

наконец, вам нужно настроить websockets для использования пользовательского обработчика рукопожатия.

@Override
void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
    stompEndpointRegistry
         .addEndpoint("/stomp") // Set websocket endpoint to connect to
         .setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler
         .withSockJS() // Add Sock JS support
}

вот и все. Теперь ваш сервер настроен на создание уникального имени участника для каждого соединения. Он пройдет этот Принципал как часть StomHeaderAccessor объекты, к которым вы можете получить доступ через прослушиватели событий подключения, функции MessageMapping и т. д...

от прослушивателей событий :

@EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
    // Get Accessor
    StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage())
}

Из Сообщения Сопоставлены API

@MessageMapping('/hello')
protected void hello(SimpMessageHeaderAccessor sha, Map message) {
    // sha available in params
}

последнее замечание об использовании convertAndSendToUser(...). При отправке сообщения пользователю, вы будете использовать что-то вроде этого

convertAndSendToUser(sha.session.principal.name, '/topic/hello', message)

однако для подписки на клиент вы будете использовать

client.subscribe('/user/topic/hello', callback)

если вы подписываете клиента на /topic/hello вы будете получать только транслируемые сообщения.


Я не делал никакой конкретной конфигурации, и я могу просто сделать это:

@MessageMapping('/hello')
protected void hello(Principal principal, Map message) {
    String username = principal.getName();
}