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

Как отправить сообщение websocket с сервера только на конкретный пользователь?

У моего webapp есть spring настройка безопасности и используется websocket. Я сталкиваюсь с сложной проблемой, пытаясь отправить сообщение с сервера на только для конкретного пользователя.

Мое понимание от чтения руководства от сервера, который мы можем сделать

simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);

И на стороне клиента:

stompClient.subscribe('/user/reply', handler);

Но я никогда не смог вызвать обратный вызов подписки. Я пробовал много разных путей, но не повезло.

Если я отправлю его в /topic/reply, он будет работать, но все остальные подключенные пользователи тоже получат его.

Чтобы проиллюстрировать проблему, я создал этот небольшой проект в github: https://github.com/gerrytan/wsproblem

Шаги для воспроизведения:

1) Клонировать и построить проект (убедитесь, что вы используете jdk 1.7 и maven 3.1)

$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run

2) Перейдите к http://localhost:8080, войдите с помощью bob/test или jim/test

3) Нажмите "Запросить определенные пользователем сообщения". Ожидается: сообщение "hello {username}" отображается рядом с "Только полученное сообщение только для меня", Actual: ничего не получено

Ответ 1

О, client side no need to known about current user, сервер сделает это для вас.

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

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

Примечание. Используя queue, а не topic, Spring всегда используя queue с sendToUser

На стороне клиента

stompClient.subscribe("/user/queue/reply", handler);

Поясните

Если какое-либо соединение с веб-сайтом открыто, Spring назначит ему session id (не HttpSession, назначить для каждого соединения). И когда ваш клиент подписался на канал, начинающийся с /user/, например: /user/queue/reply, ваш экземпляр сервера будет подписаться на очередь с именем queue/reply-user[session id]

Когда используется сообщение отправки пользователю, например: username admin Вы напишете simpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

Spring определит, какой session id отображается пользователю admin. Например: найдено два сеанса wsxedc123 и thnujm456, Spring переведет его в 2 адресата queue/reply-userwsxedc123 и queue/reply-userthnujm456, и он отправит ваше сообщение с двумя получателями в ваш брокер сообщений.

Брокер сообщений получает сообщения и возвращает их обратно на ваш экземпляр сервера, который проводит сеанс, соответствующий каждому сеансу (сеансы WebSocket могут удерживаться одним или несколькими серверами). Spring преобразует сообщение в destination (например: user/queue/reply) и session id (например: wsxedc123). Затем он отправляет сообщение соответствующему Websocket session

Ответ 2

А я нашел свою проблему. Сначала я не зарегистрировал префикс /user на простом брокере

<websocket:simple-broker prefix="/topic,/user" />

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

convertAndSendToUser(principal.getName(), "/reply", reply);

Spring автоматически добавит "/user/" + principal.getName() к месту назначения, поэтому он переходит в "/user/bob/reply".

Это также означает, что в javascript я должен был подписаться на другой адрес для пользователя

stompClient.subscribe('/user/' + userName + '/reply,...) 

Ответ 3

Мое решение этого на основе Thanh Nguyen Van лучшее объяснение, но, кроме того, я настроил MessageBrokerRegistry:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/queue/", "/topic/");
        ...
    }
    ...
}

Ответ 4

Я создал образец проекта websocket, используя STOMP. Я замечаю, что

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic", "/queue");// including /user also works
    config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/getfeeds").withSockJS();
}

}

он работает независимо от того, включен ли "/user" в config.enableSimpleBroker(...

Ответ 5

Точно так же я сделал то же самое, и он работает без использования пользователя

@Configuration
@EnableWebSocketMessageBroker  
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
       registry.addEndpoint("/gs-guide-websocket").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic" , "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }
}