Как отправить сообщение websocket с сервера только конкретному пользователю?
Мое веб-приложение имеет настройку безопасности Spring и использует веб-сокет. У меня возникла сложная проблема при попытке отправить сообщение с сервера только конкретному пользователю .
Я понял, что читал руководство , с сервера, который мы можем сделать
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}" отображается рядом с "Received Message To Me Only" только для этого пользователя, Фактически: ничего не получено
источник
simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);
и когда сообщение отправлено с сервера, оно выдает это исключениеjava.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
convertAndSendToUser(principal.getName(), "/reply", reply);
Ответы:
О,
client side no need to known about current user
сервер сделает это за вас.На стороне сервера, используя следующий способ отправки сообщения пользователю:
simpMessagingTemplate.convertAndSendToUser(username, "/queue/reply", message);
Примечание. Использование Spring
queue
, а неtopic
использование всегдаqueue
сsendToUser
На стороне клиента
stompClient.subscribe("/user/queue/reply", handler);
Объясните
Когда открыто какое-либо соединение с веб-сокетом, Spring назначит ему
session id
(неHttpSession
назначается для каждого соединения). И когда ваш клиент подписывается на канал, начинающийся/user/
, например, с:,/user/queue/reply
ваш экземпляр сервера будет подписываться на очередь с именемqueue/reply-user[session id]
При использовании отправки сообщения пользователю, например: имя пользователя -
admin
Вы напишитеsimpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
Spring определит, какой из них
session id
сопоставлен пользователюadmin
. Например: Он нашел две сессииwsxedc123
иthnujm456
весна будет перевести его на 2 пункта назначенияqueue/reply-userwsxedc123
иqueue/reply-userthnujm456
, и отправить сообщение с 2 направления к вашему сообщению брокера.Брокер сообщений получает сообщения и передает их обратно вашему экземпляру сервера, который поддерживает сеанс, соответствующий каждому сеансу (сеансы WebSocket могут удерживаться одним или несколькими серверами). Spring переведет сообщение в
destination
(например:)user/queue/reply
иsession id
(например:)wsxedc123
. Затем он отправляет сообщение соответствующемуWebsocket session
источник
HttpSession
при инициировании подключения к веб-сокетуDefaultHandshakeHandler
и переопределять методdetermineUser
queue/reply-user[session id]
часть в официальном документе?Ах, я нашел, в чем была моя проблема. Сначала
/user
на простом брокере приставку не регистрировал<websocket:simple-broker prefix="/topic,/user" />
Тогда мне не нужен лишний
/user
префикс при отправке:convertAndSendToUser(principal.getName(), "/reply", reply);
Весна автоматически добавится
"/user/" + principal.getName()
к месту назначения, поэтому он преобразуется в "/ user / bob / reply".Это также означает, что в javascript мне приходилось подписываться на разные адреса для каждого пользователя.
stompClient.subscribe('/user/' + userName + '/reply,...)
источник
@RequestMapping
а также@MessageMapping
см. здесь Как подписаться с использованием интеграции Sping Websocket для определенного имени пользователя (userId) + получать уведомления от метода, аннотированного с помощью @RequestMapping?Я также создал образец проекта веб-сокета, используя 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 (...
источник
Мое решение этого вопроса основано на лучшем объяснении Тхань Нгуен Вана, но, кроме того, я настроил MessageBrokerRegistry:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/queue/", "/topic/"); ... } ... }
источник
Точно я сделал то же самое, и он работает без использования пользователя
@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"); } }
источник
/app
? В таком случае?