サーバーから特定のユーザーのみにwebsocketメッセージを送信する方法は?
私のwebappには、春のセキュリティ設定があり、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}」が「自分宛の受信メッセージのみ」の横に表示されます。実際:何も受信されません
ああ、_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);
_
説明
Websocket接続が開いている場合、Springは_session id
_(HttpSession
ではなく、接続ごとに割り当て)を割り当てます。そして、クライアントが_/user/
_で始まるチャンネルにサブスクライブするとき、例えば:_/user/queue/reply
_、サーバーインスタンスは_queue/reply-user[session id]
_という名前のキューにサブスクライブします
ユーザーへのメッセージ送信を使用する場合、例:ユーザー名はadmin
であるsimpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);
と書く
Springは、どの_session id
_がユーザーadmin
にマッピングされるかを決定します。たとえば、2つのセッション_wsxedc123
_および_thnujm456
_が見つかったため、Springはそれを2つの宛先_queue/reply-userwsxedc123
_および_queue/reply-userthnujm456
_に変換し、2つの宛先を持つメッセージをメッセージブローカーに送信します。
メッセージブローカーはメッセージを受信し、各セッションに対応するセッションを保持しているサーバーインスタンスにそれを返します(WebSocketセッションは1つ以上のサーバーで保持できます)。 Springはメッセージをdestination
(例:_user/queue/reply
_)および_session id
_(例:_wsxedc123
_)に変換します。次に、対応する_Websocket session
_にメッセージを送信します
ああ、私は私の問題が何であるかを見つけました。最初に、単純なブローカーに_/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,...)
_
まったく同じことをしましたが、ユーザーを使用せずに動作しています
@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");
}
}
Thanh Nguyen Vanの最良の説明に基づいた私のソリューションですが、さらにMessageBrokerRegistryを構成しました。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/", "/topic/");
...
}
...
}
STOMPを使用してサンプルwebsocketプロジェクトも作成しました。私が気づいているのは
@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(...