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?
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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With