Spring Websocket üzerinde belirli bir kullanıcıya mesaj gönderme


87

Sunucudan yalnızca belirli bir kullanıcıya websocket mesajı nasıl gönderilir?

Web uygulamamın yay güvenliği ayarı var ve websocket kullanıyor. Sunucudan yalnızca belirli bir kullanıcıya mesaj göndermeye çalışırken zor bir sorunla karşılaşıyorum .

Kılavuzu okumaktan anladığım kadarıyla yapabileceğimiz sunucudan

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

Ve müşteri tarafında:

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

Ancak abonelik geri aramasını hiçbir zaman başlatamadım. Pek çok farklı yolu denedim ama şansım yok.

/ Topic / response'a gönderirsem çalışır, ancak diğer tüm bağlı kullanıcılar da alır.

Sorunu göstermek için bu küçük projeyi github'da oluşturdum: https://github.com/gerrytan/wsproblem

Yeniden üretme adımları:

1) Projeyi klonlayın ve oluşturun (jdk 1.7 ve maven 3.1 kullandığınızdan emin olun)

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

2) http://localhost:8080Bob / test veya jim / test kullanarak giriş yapın.

3) "Kullanıcıya özel mesaj iste" yi tıklayın. Beklenen: Yalnızca bu kullanıcı için "Yalnızca Bana Mesaj Alındı" nın yanında "merhaba {kullanıcı adı}" mesajı görüntülenir, Gerçek: hiçbir şey alınmadı


ConvertAndSendToUser'a (String kullanıcısı, String hedefi, T mesajı) mı bakıyordunuz? docs.spring.io/spring/docs/4.0.0.M3/javadoc-api/org/…
Viktor K.

Evet bunu da denedim ama şans yok
gerrytan

Özel proje üzerinde çalışıyorum ve kullandığımız yöntem bu ve bizim için çalışıyor. Sanırım olası bir sorun, "/ kullanıcı / yanıt" a abone olmanız ve "/ kullanıcı / {kullanıcı adı} / yanıt" adresine mesaj göndermeniz olabilir. {Username} bölümünü kaldırmanız ve convertAndSendToUser'ı (String kullanıcısı, String hedefi, T mesajı) kullanmanız gerektiğini düşünüyorum.
Viktor K.

Teşekkürler ama denedim simpMessagingTemplate.convertAndSendToUser(principal.getName(), "/user/reply", reply);ve mesaj sunucudan gönderildiğinde bu istisnayı atıyorjava.lang.IllegalArgumentException: Expected destination pattern "/principal/{userId}/**"
gerrytan

@ViktorK. doğru ve doğru çözüme oldukça yaklaştınız. İstemci tarafındaki aboneliğiniz doğruydu, sadece denemeniz gerekiyordu:convertAndSendToUser(principal.getName(), "/reply", reply);
Tip-Sy

Yanıtlar:


85

Oh, client side no need to known about current usersunucu bunu sizin için yapacak.

Sunucu tarafında, bir kullanıcıya mesaj göndermek için aşağıdaki yolu kullanarak:

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

Not: Kullanarak queuedeğil topic, Bahar hep kullanılarak queueilesendToUser

İstemci tarafında

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

Açıklamak

Herhangi bir websocket bağlantısı açık olduğunda, Spring ona bir atayacaktır session id( HttpSessionbağlantı başına atama). Ve müşteriniz bir kanala abone olduğunda /user/, örneğin:, /user/queue/replysunucu örneğiniz adlı bir kuyruğa abone olurqueue/reply-user[session id]

Kullanıcıya mesaj gönder kullanırken, örneğin: kullanıcı adı admin yazacaksınsimpMessagingTemplate.convertAndSendToUser("admin", "/queue/reply", message);

Spring, hangisinin session idkullanıcıya eşlendiğini belirleyecektir admin. Örneğin: İki oturumu bulundu wsxedc123ve thnujm456Bahar 2 hedefe tercüme ederiz, queue/reply-userwsxedc123ve queue/reply-userthnujm456, ve o mesaj brokera 2 hedeflere mesajınızı gönderin.

Message Broker, iletileri alır ve her oturuma karşılık gelen oturumun bekletildiği sunucu örneğinize geri sağlar (WebSocket oturumları bir veya daha fazla sunucu tarafından tutulabilir). Spring, mesajı destination(örneğin:) user/queue/replyve session id(örneğin :) dillerine çevirecektir wsxedc123. Ardından mesajı ilgili kişiye gönderirWebsocket session


7
Kullanıcı adını nasıl bildiğinizi açıklar mısınız? örneğinizde kullanıcı adının 'admin' olduğunu söylediniz, kullanıcı adını user'dan mı alıyorsunuz?
Jorj

1
Kullanıcı adı, HttpSessionbir websocket bağlantısı başlatıldığında alındı
Thanh Nguyen Van

1
lütfen kullanıcı adının nasıl ayarlanacağı hakkında daha fazla bilgi verebilir misiniz? kullanıcı adını abonelik mesajına gönderebilir miyim?
Andres

1
@Andres: DefaultHandshakeHandlerYöntemini genişletebilir ve geçersiz kılabilirsinizdetermineUser
Thanh Nguyen Van

1
Açıklamanız harika çalışıyor. Ancak queue/reply-user[session id]resmi belgede yer alan kısım nerede ?
hbrls

35

Ah sorunumun ne olduğunu buldum. İlk önce /userbasit komisyoncuya öneki kaydetmedim

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

O /userhalde gönderirken fazladan öneke ihtiyacım yok :

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

Yay, otomatik olarak "/user/" + principal.getName()hedefin başına eklenecektir , dolayısıyla "/ user / bob / response" şeklinde çözülecektir.

Bu aynı zamanda javascript'te kullanıcı başına farklı adreslere abone olmam gerektiği anlamına geliyor

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

3
Mmm ... userName istemci tarafını kurmaktan kaçınmanın bir yolu var mı? Bu değeri değiştirerek (örneğin başka bir kullanıcı adı kullanarak), başkalarının mesajlarını görebileceksiniz.
vdenotaris


ek açıklamalı yöntemlerden kullanıcıya yanıt vermek için nasıl abone olun@RequestMapping ve ayrıca @MessageMappingburaya bakın Sping Websocket entegrasyonunu kullanarak belirli userName (userId) üzerinde nasıl abone olunur + @RequestMapping ile anotasyonlu yöntemden bildirim alın?
Shantaram Tupe

Hey arkadaşım bana bunu söyleyebilir misin? Bu "kullanıcı" da nedir? Spring Security kullanıyorum ve kullanıcılarım (kullanıcı adı) e-posta adresleridir. Her kullanıcı oturum açtığında JWT jetonunu Spring Security'de alır.
Francisco Souza

Aynı problemlerle karşılaştım, cevabına gelmeden önce saatlerce aradım. SADECE açıklamanız bana yardımcı oldu. Çok teşekkür ederim!!
Navaneeth

3

STOMP kullanarak da örnek bir websocket projesi oluşturdum. Fark ettiğim şey bu

@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();
}

}

config.enableSimpleBroker (...


2

Benim çözümüm Thanh Nguyen Van'ın en iyi açıklamasına dayanıyor, ancak ek olarak MessageBrokerRegistry'i yapılandırdım:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

2

Ben de aynısını yaptım ve kullanıcı kullanmadan çalışıyor

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

Hey, bunu nerede kullanıyorsun /app ? Bu durumda?
Francisco Souza
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.