8

I'm developing a realtime notification system in Spring 4 by using a build-in Message Broker, and STOMP over WebSocket.

I would like to be able to send messages to a specific user, according with his username. In order to achieve this goal, I'm using the convertAndSendToUser method of org.springframework.messaging.simp.SimpMessagingTemplate class, as follows:

private final MessagingTemplate messagingTemplate;

@Autowired
public LRTStatusListener(SimpMessagingTemplate messagingTemplate) {
    this.messagingTemplate = messagingTemplate;
}


@Scheduled(fixedDelay=5000)
public void sendMessages(Principal principal)
    messagingTemplate
        .convertAndSendToUser(principal.getName(), "/horray", "Horray, " + principal.getName() + "!");
}

As configuration:

@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/notifications").withSockJS();
    }

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

}

Client-side (via JavaScript), I should subscribe to a channel by specifing the username (according with another very similar question: Sending message to specific user on Spring Websocket).

stompClient.subscribe('/user/' + username + '/horray, ...) 

This last point sounds weird...

Supposing that I'm logged as w.white on my webapp, by subscribing:

stompClient.subscribe('/user/w.white/horray, ...)

... I will be able to see messages sent to w.white, and this is awesome... But subscribing:

stompClient.subscribe('/user/j.pinkman/horray, ...)

... I will be able to see also messages sent to j.pinkman, despide that I'm currently logged as w.white.

It is a way to overcome this problem?


Update

Below there is the log about the connection over WebSocket:

Opening Web Socket... 
Web Socket Opened... 
>>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000

<<< CONNECTED
user-name:w.white
heart-beat:0,0
version:1.1

connected to server undefined
Connected: CONNECTED
version:1.1
heart-beat:0,0
user-name:w.white

>>> SUBSCRIBE
id:sub-0
destination:/topic/lrt

>>> SUBSCRIBE
id:sub-1
destination:/user/lrt

2 Answers 2

26

I found the solution.

First of all, it is important to know that the /user channel is already managed by Spring STOMP, and by the way, no registration is required.

So:

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

Then, I setup the destination channel as /queue/horray:

@Scheduled(fixedDelay=5000)
public void sendMessages(Principal principal)
    messagingTemplate
        .convertAndSendToUser(principal.getName(), "/queue/horray", "Horray, " + principal.getName() + "!");
}

At last, on client:

stompClient.subscribe('/user/queue/horray', '...');

Now, it works fine! Messages are sent only to the specified recipient, according to the Principal fetched by the security context.

Sign up to request clarification or add additional context in comments.

8 Comments

Congratulations on your solution. Do you know if there would be a similar version using the @SendToUser annotation?
My conf almost same with yours (I'm using queue/reply instead of queue/horray) but my console output is empty. Did you skip some important points in your post or that's all? Could you post total config?
Hello, @vdenotaris. I have noticed your scheduled method for publishing messages: @Scheduled(fixedDelay=5000) public void sendMessages(Principal principal) messagingTemplate .convertAndSendToUser(principal.getName(), "/queue/horray", "Horray, " + principal.getName() + "!"); } Spring scheduled methods do not work with parameters. How can I get Principal inside the scheduled method? Thanks in advance!
Any method annotated with @Scheduled can not accept any argument. @Scheduled(fixedDelay=5000) public void sendMessages(Principal principal){} this will not work.
@vdenotaris I'm not using Spring security :-(, How do I get user here messagingTemplate .convertAndSendToUser(?, "/queue/horray", "Horray, ?");
|
7

Since users on my application are not authenticated I just used the session Id to differenciate the various topics

on the server:

template.convertAndSend("/topic/warnings/" + sessionId, ...)

And the client is pretty straightforward

stompClient.subscribe('/topic/warnings/${pageContext.session.id}', ...

Maybe not the cleanest way but it works, and without authentication I couldn't make use of /user channel

2 Comments

I my case If two users are logged into the same browser, their sessionId will be the same, so one can see other's subscriptions, this should be done on user_id or user_name...
@ShantaramTupe two users are logged into the same browser is not a valid case IMHO. In that case you will have to switch the sessions internally. Then again, practically and ultimately it won't be multiple users, but a single user having multiple accounts. (Gmail for an instance, you could think of)..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.