Spring Websocket에서 특정 사용자에게 메시지 보내기


85

서버에서 특정 사용자에게만 웹 소켓 메시지를 보내는 방법은 무엇입니까?

내 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:8080bob / test 또는 jim / test를 사용하여 로그인합니다.

3) "사용자 별 메시지 요청"을 클릭합니다. 예상 : "안녕하세요 {username}"메시지가이 사용자에 대해서만 "나에게만 수신 됨"옆에 표시됨, 실제 : 아무것도 수신되지 않음


convertAndSendToUser (String user, String destination, T message)를 보셨습니까? docs.spring.io/spring/docs/4.0.0.M3/javadoc-api/org/...
빅토르 K.

예 너무하지만 운이 시도하지
gerrytan

저는 개인 프로젝트를 진행하고 있으며 이것이 우리가 사용하는 방법이며 우리를 위해 일하고 있습니다. 잠재적 인 문제 중 하나는 "/ 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

@ViktorK. 옳았으며 올바른 솔루션에 매우 가깝습니다. 클라이언트 측 구독이 정확했습니다. 다음을 시도해보십시오.convertAndSendToUser(principal.getName(), "/reply", reply);
Tip-Sy

답변:


81

아, client side no need to known about current user서버가 대신 해줄 것입니다.

서버 측에서 다음 방법을 사용하여 사용자에게 메시지를 보냅니다.

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

참고 : 사용 queue하지 않고 topic항상 사용 queue하는 SpringsendToUser

클라이언트 측

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입니다. 예 : 그것은 두 개의 세션을 발견 wsxedc123하고 thnujm456봄이 목적지로 변환합니다, queue/reply-userwsxedc123그리고 queue/reply-userthnujm456, 그것은 당신의 메시지 브로커 2 개 목적지로 메시지를 보낼 수 있습니다.

메시지 브로커는 메시지를 수신하고 각 세션에 해당하는 세션을 보유하는 서버 인스턴스에 다시 제공합니다 (WebSocket 세션은 하나 이상의 서버에서 보유 할 수 있음). Spring은 메시지를 destination(예 :) user/queue/replysession id(예 :)로 번역합니다 wsxedc123. 그런 다음 해당 메시지를Websocket session


7
사용자 이름을 어떻게 아는지 설명해 주시겠습니까? 귀하의 예에서 사용자 이름이 'admin'이라고 말했는데 사용자로부터 사용자 이름을 받았습니까?
Jorj

1
사용자 이름이로부터 얻은 HttpSession웹 소켓 연결을 시작할 때
탄 응웬 반에게

1
사용자 이름 설정 방법에 대한 자세한 정보를 제공해 주시겠습니까?. 어쨌든 구독 메시지에 사용자 이름을 보낼 수 있습니까?
Andres

1
@Andres : DefaultHandshakeHandler메서드를 확장 하고 재정의 할 수 있습니다.determineUser
Thanh Nguyen Van

1
귀하의 설명은 훌륭합니다. 그러나 queue/reply-user[session id]공식 문서 의 부분 은 어디에 있습니까?
hbrls dec.

35

아 내 문제가 뭔지 찾았 어. 먼저 /user간단한 브로커에 접두사를 등록하지 않았습니다.

<websocket:simple-broker prefix="/topic,/user" />

그러면 /user보낼 때 추가 접두사 가 필요하지 않습니다 .

convertAndSendToUser(principal.getName(), "/reply", reply);

Spring은 자동으로 "/user/" + principal.getName()목적지 앞에 추가 되므로 "/ user / bob / reply"로 해석됩니다.

이것은 또한 자바 스크립트에서 사용자마다 다른 주소를 구독해야 함을 의미합니다.

stompClient.subscribe('/user/' + userName + '/reply,...) 

3
음 ... userName 클라이언트 측 설정을 피하는 방법이 있습니까? 해당 값을 변경하면 (예 : 다른 사용자 이름 사용) 다른 사람의 메시지를 볼 수 있습니다.
vdenotaris 2014 년


어떻게 가입 주석 방법에서 사용자에게 응답 할 수 @RequestMapping있으며 @MessageMapping여기에서 볼 @RequestMapping과 anotated 방법에서 특정 이름 (userId를) + GET 알림 스핑 웹 소켓 통합을 사용하여 가입하는 방법?
Shantaram Tupe

친구 야 이거 말해 줄래? 어쨌든이 "사용자"는 무엇입니까? 저는 Spring Security를 ​​사용하고 있으며 내 사용자 (사용자 이름)는 이메일 주소입니다. 각 사용자가 로그인하면 Spring Security에서 JWT 토큰을 얻습니다.
Francisco Souza

나는 당신의 대답을 찾기 전에 몇 시간 동안 같은 문제에 직면했습니다. 당신의 설명 만이 나를 도왔습니다. 정말 고맙습니다!!
Navaneeth

3

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


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? 어떤 경우에?
Francisco Souza
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.