Отправка сообщения конкретному пользователю по адресу 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");
}
}