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


84

サーバーから特定のユーザーにのみWebSocketメッセージを送信する方法は?

私のwebappには春のセキュリティ設定があり、websocketを使用しています。サーバーから特定のユーザーにのみメッセージを送信しようとすると、トリッキーな問題が発生します。

マニュアルを読んだことからの私の理解は、私たちができるサーバーからです

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

そしてクライアント側では:

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

しかし、サブスクリプションコールバックを呼び出すことはできませんでした。私は多くの異なる道を試しましたが、運がありません。

/ topic / replyに送信すると機能しますが、接続されている他のすべてのユーザーも受信します。

問題を説明するために、私はgithubでこの小さなプロジェクトを作成しました:https//github.com/gerrytan/wsproblem

再現する手順:

1)プロジェクトのクローンを作成してビルドします(jdk1.7とmaven3.1を使用していることを確認してください)

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

2)に移動しhttp://localhost:8080、bob / testまたはjim / testのいずれかを使用してログインします

3)[ユーザー固有のメッセージをリクエストする]をクリックします。期待:このユーザーのみの「ReceivedMessage To Me Only」の横に、「hello {username}」というメッセージが表示されます。実際:何も受信されません。


convertAndSendToUser(String user、String destination、T message)を見たことがありますか?docs.spring.io/spring/docs/4.0.0.M3/javadoc-api/org/…–
Viktor K.

はい、それも試しましたが、運がありません
gerrytan 2014年

私はプライベートプロジェクトに取り組んできましたが、これは私たちが使用している方法であり、私たちのために働いています。潜在的な問題の1つは、「/ user / reply」をサブスクライブし、「/ user / {username} / reply」にメッセージを送信していることだと思います。{username}の部分を削除し、convertAndSendToUser(String user、String destination、T message)を使用する必要があると思います。
Viktor K.

ありがとう、しかし私は試しましsimpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);た、そしてメッセージがサーバーから送られるとき、それはこの例外を投げますjava.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
gerrytan 2014年

@ViktorK。正しいです、そしてあなたは正しい解決策にかなり近かったです。:クライアント側でのサブスクリプションを使用すると、単純にしようとしていた、正しかったconvertAndSendToUser(principal.getName(), "/reply", reply);
ヒント-SY

回答:


81

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

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

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

注:Springは常にwithを使用するのqueueではなくtopic、を使用queueするsendToUser

クライアント側

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

説明する

WebSocket接続が開いている場合、Springはそれに割り当てますsession idHttpSession接続ごとに割り当てません)。また、クライアントが次のように始まるチャネルにサブスクライブすると/user//user/queue/replyサーバーインスタンスは次の名前のキューにサブスクライブします。queue/reply-user[session id]

ユーザーにメッセージを送信を使用する場合、例:username is admin You will writesimpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

Springは、どちらsession idがユーザーにマップされるかを決定しますadmin。例えば:それは2つのセッションを見つけたwsxedc123し、thnujm456春は2宛先にそれを翻訳します、queue/reply-userwsxedc123そしてqueue/reply-userthnujm456、それはあなたのメッセージブローカへの2つの都市で、あなたのメッセージを送信します。

メッセージブローカーはメッセージを受信し、各セッションに対応する保持セッションをサーバーインスタンスに返します(WebSocketセッションは1つ以上のサーバーで保持できます)。春はへのメッセージを翻訳しますdestination(例:user/queue/reply)とsession id(例:wsxedc123)。次に、対応するにメッセージを送信しますWebsocket session


7
ユーザー名をどのように知っているか説明していただけますか?あなたの例では、ユーザー名は「admin」であると言いましたが、ユーザーからユーザー名を受け取りますか?
Jorj 2016

1
ユーザー名はHttpSession、WebSocket接続を開始したときから取得されました
Thanh Nguyen Van

1
ユーザー名の設定方法について詳しく教えてください。とにかく私はサブスクリプションメッセージでユーザー名を送信できますか?
アンドレス

1
@Andres:DefaultHandshakeHandlerメソッドを拡張してオーバーライドできますdetermineUser
Thanh Nguyen Van

1
あなたの説明はうまくいきます。しかしqueue/reply-user[session id]、公式文書のどこにあるのでしょうか?
hbrls 2017

35

ああ、私は自分の問題が何であるかを見つけました。最初に私は/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
うーん... userNameクライアント側の設定を回避する方法はありますか?その値を変更することにより(たとえば、別のユーザー名を使用することにより)、他の人のメッセージを見ることができます。
vdenotaris 2014

2
私の解決策を参照してください:stackoverflow.com/questions/25646671/...は
vdenotaris

どのようにサブスクライブして注釈を付け方法の中からユーザーに返信するために@RequestMapping@MessageMappingここを参照してください@RequestMappingとanotated方法から特定のユーザー名(USERID)+ GET通知にSPINGのWebSocketの統合を使用して購読する方法は?
シャンタラムチュープ2017

ねえ私の友人は私にこれを教えてもらえますか?とにかくこの「ユーザー」とは何ですか?Spring Securityを使用しており、ユーザー(ユーザー名)は電子メールアドレスです。各ユーザーがログインすると、SpringSecurityでJWTトークンを取得します。
FranciscoSouza19年

私は同じ問題に直面し、あなたの答えに来る前に何時間も検索しました。あなたの説明だけが私を助けました。どうもありがとうございます!!
ナバニース

3

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

Thanh Nguyen Vanの最良の説明に基づいた私の解決策ですが、さらにMessageBrokerRegistryを構成しました。

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

2

まさに私は同じことをしました、そしてそれはユーザーを使わずに働いています

@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どこで使うの?その場合?
FranciscoSouza19年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.