web-dev-qa-db-ja.com

Spring Websocketで特定のユーザーにメッセージを送信する

サーバーから特定のユーザーのみに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}」が「自分宛の受信メッセージのみ」の横に表示されます。実際:何も受信されません

67
gerrytan

ああ、_client side no need to known about current user_、サーバーがあなたのためにそれをします。

サーバー側で、次の方法を使用してユーザーにメッセージを送信します。

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

注:queueではなくtopicを使用し、Springは常にqueuesendToUserとともに使用します

クライアント側

_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_にメッセージを送信します

64

ああ、私は私の問題が何であるかを見つけました。最初に、単純なブローカーに_/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,...) 
_
28
gerrytan

まったく同じことをしましたが、ユーザーを使用せずに動作しています

@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");
    }
}
2
Sid

Thanh Nguyen Vanの最良の説明に基づいた私のソリューションですが、さらにMessageBrokerRegistryを構成しました。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

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(...

2
Kans