web-dev-qa-db-ja.com

「ユーザー」は、convertAndSendToUserがSockJS + Spring Websocketで機能する場所はどこですか?

Spring SockJS + WebsocketフレームワークでconvertAndSendToUserがどのように機能するかを理解したいと思います。

クライアントでは、次のように接続します

stompClient.connect(login, password, callback())

これにより、ログインとパスワードの「Stompクレデンシャル」を含む接続要求が発生します。 SessionConnectEventを処理する場合 http://www.sergialmar.com/2014/03/detect-websocket-connects-and-disconnects-in-spring-4/

しかし、これがキューへのサーバー側の送信操作で意味する「ユーザー」になるかどうかは、私には不明確なままです。

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

私が得ることができる最も近いのは、このスレッドを読むことです Spring Websocketの特定のユーザーにメッセージを送信する 、Thanh Nguyen Vanの回答ですが、それでもまだ不明です。

基本的に私がする必要があるのは、いくつかのクライアントを同じトピックにサブスクライブすることですが、サーバー上で、それらに異なるデータを送信します。クライアントはユーザーIDを提供できます。

29
Askar Ibragimov

私はWebSocketを理解しようとしていて、この質問に出くわしました。答えを見つけられなかったことに徹底的に失望した。これは将来の読者のためのものです。

私は、読者がStompを使用したSpring WebSocketの基本を理解していることを前提としています。サブスクリプション、宛先プレフィックス、トピック、ソケット構成ファイルなどの用語が理解されます。

クライアントがサブスクライブしているトピックプレフィックスを使用して、ストンプサーバーからクライアントにメッセージを送信できることはわかっています。 _/topic/hello_。 SpringはconvertAndSendToUser(username, destination, message) AP​​Iを提供しているため、特定のユーザーにメッセージを送信できることもわかっています。これは文字列のユーザー名を受け入れます。つまり、すべての接続に何らかの方法で一意のユーザー名がある場合、トピックに登録している特定のユーザーにメッセージを送信できるはずです。

あまり理解されていないのは、このユーザー名はどこから来たのですか?

このユーザー名は_Java.security.Principal_インターフェースの一部です。StompHeaderAccessorまたはWebSocketSessionオブジェクトにはこのプリンシパルのインスタンスがあり、そこからユーザー名を取得できます。しかし、私の実験によると、それは自動的に生成されません。 セッションごとにサーバーが手動で生成する必要があります。

このインターフェースを最初に使用するには、それを実装する必要があります。

_class StompPrincipal implements Principal {
    String name

    StompPrincipal(String name) {
        this.name = name
    }

    @Override
    String getName() {
        return name
    }
}
_

次に、DefaultHandshakeHandlerをオーバーライドすることにより、接続ごとに一意のStompPrincipalを生成できます。任意のロジックを使用してユーザー名を生成できます。 UUIDを使用する1つの潜在的なロジックを次に示します。

_class CustomHandshakeHandler extends DefaultHandshakeHandler {
    // Custom class for storing principal
    @Override
    protected Principal determineUser(ServerHttpRequest request,
                                      WebSocketHandler wsHandler,
                                      Map<String, Object> attributes) {
        // Generate principal with UUID as name
        return new StompPrincipal(UUID.randomUUID().toString())
    }
}
_

最後に、カスタムハンドシェイクハンドラーを使用するようにWebSocketを構成する必要があります。

_@Override
void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
    stompEndpointRegistry
         .addEndpoint("/stomp") // Set websocket endpoint to connect to
         .setHandshakeHandler(new CustomHandshakeHandler()) // Set custom handshake handler
         .withSockJS() // Add Sock JS support
}
_

それでおしまい。これで、すべての接続に対して一意のプリンシパル名を生成するようにサーバーが構成されました。このプリンシパルは、接続イベントリスナー、MessageMapping関数などを介してアクセスできるStomHeaderAccessorオブジェクトの一部として渡されます。

イベントリスナーから:

_@EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
    // Get Accessor
    StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage())
}
_

メッセージマップAPIから

_@MessageMapping('/hello')
protected void hello(SimpMessageHeaderAccessor sha, Map message) {
    // sha available in params
}
_

convertAndSendToUser(...)の使用に関する最後の注意点。ユーザーにメッセージを送信するときは、次のようなものを使用します

_convertAndSendToUser(sha.session.principal.name, '/topic/hello', message)
_

ただし、クライアントをサブスクライブするには、次を使用します

_client.subscribe('/user/topic/hello', callback)
_

クライアントを_/topic/hello_にサブスクライブすると、ブロードキャストメッセージのみが受信されます。

21
Siddharth Garg

私は特定の構成を行わず、これを行うことができます:

@MessageMapping('/hello')
protected void hello(Principal principal, Map message) {
    String username = principal.getName();
}
0
Wenneguen