Allow use of @SendToUser even w/o authenticated user
Before this change, subscribing to a user destination and use of
@SendToUser annotation required an authenticated user.
This change makes it possible to subscribe to a user destination from
WebSocket sessions without an authenticated user. In such cases the
destination is associated with one session only rather than with a
user (and all their sessions).
It is then also possible to send a message to a user destination
via "/user/{sessionId}/.." rather than "/user/{user}/...".
That means @SendToUser works relying on the session id of the input
message, effectively sending a reply to destination private to the
session.
A key use case for this is handling an exception with an
@MessageExceptionHandler method and sending a reply with @SendToUser.
Issue: SPR-11309
This commit is contained in:
@@ -332,6 +332,26 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
verifyNoMoreInteractions(messagingTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendToUserSessionWithoutUserName() throws Exception {
|
||||
|
||||
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, null);
|
||||
this.handler.handleReturnValue(PAYLOAD, this.sendToUserReturnType, inputMessage);
|
||||
|
||||
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
|
||||
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(this.messageCaptor.getAllValues().get(0));
|
||||
assertEquals("/user/sess1/dest1", headers.getDestination());
|
||||
assertEquals("sess1", headers.getSessionId());
|
||||
|
||||
headers = SimpMessageHeaderAccessor.wrap(this.messageCaptor.getAllValues().get(1));
|
||||
assertEquals("/user/sess1/dest2", headers.getDestination());
|
||||
assertEquals("sess1", headers.getSessionId());
|
||||
}
|
||||
|
||||
|
||||
private Message<?> createInputMessage(String sessId, String subsId, String destinationPrefix,
|
||||
String destination, Principal principal) {
|
||||
|
||||
@@ -29,7 +29,8 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link org.springframework.messaging.simp.user.DefaultUserDestinationResolver}.
|
||||
* Unit tests for
|
||||
* {@link org.springframework.messaging.simp.user.DefaultUserDestinationResolver}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@@ -81,6 +82,19 @@ public class DefaultUserDestinationResolverTests {
|
||||
assertEquals("/queue/foo-user123", actual.getTargetDestinations().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleSubscribeNoUser() {
|
||||
String sourceDestination = "/user/queue/foo";
|
||||
Message<?> message = createMessage(SimpMessageType.SUBSCRIBE, null, SESSION_ID, sourceDestination);
|
||||
UserDestinationResult actual = this.resolver.resolveDestination(message);
|
||||
|
||||
assertEquals(sourceDestination, actual.getSourceDestination());
|
||||
assertEquals(1, actual.getTargetDestinations().size());
|
||||
assertEquals("/queue/foo-user" + SESSION_ID, actual.getTargetDestinations().iterator().next());
|
||||
assertEquals(sourceDestination, actual.getSubscribeDestination());
|
||||
assertNull(actual.getUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUnsubscribe() {
|
||||
Message<?> message = createMessage(SimpMessageType.UNSUBSCRIBE, this.user, SESSION_ID, "/user/queue/foo");
|
||||
@@ -116,6 +130,19 @@ public class DefaultUserDestinationResolverTests {
|
||||
assertEquals("/queue/foo-useropenid123", actual.getTargetDestinations().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleMessageWithNoUser() {
|
||||
String sourceDestination = "/user/" + SESSION_ID + "/queue/foo";
|
||||
Message<?> message = createMessage(SimpMessageType.MESSAGE, null, SESSION_ID, sourceDestination);
|
||||
UserDestinationResult actual = this.resolver.resolveDestination(message);
|
||||
|
||||
assertEquals(sourceDestination, actual.getSourceDestination());
|
||||
assertEquals(1, actual.getTargetDestinations().size());
|
||||
assertEquals("/queue/foo-user123", actual.getTargetDestinations().iterator().next());
|
||||
assertEquals("/user/queue/foo", actual.getSubscribeDestination());
|
||||
assertNull(actual.getUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreMessage() {
|
||||
|
||||
@@ -129,11 +156,6 @@ public class DefaultUserDestinationResolverTests {
|
||||
actual = this.resolver.resolveDestination(message);
|
||||
assertNull(actual);
|
||||
|
||||
// subscribe + no user
|
||||
message = createMessage(SimpMessageType.SUBSCRIBE, null, SESSION_ID, "/user/queue/foo");
|
||||
actual = this.resolver.resolveDestination(message);
|
||||
assertNull(actual);
|
||||
|
||||
// subscribe + not a user destination
|
||||
message = createMessage(SimpMessageType.SUBSCRIBE, this.user, SESSION_ID, "/queue/foo");
|
||||
actual = this.resolver.resolveDestination(message);
|
||||
|
||||
@@ -108,10 +108,6 @@ public class UserDestinationMessageHandlerTests {
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.MESSAGE, "joe", "123", "/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// subscribe + no user
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.SUBSCRIBE, null, "123", "/user/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
// subscribe + not a user destination
|
||||
this.messageHandler.handleMessage(createMessage(SimpMessageType.SUBSCRIBE, "joe", "123", "/queue/foo"));
|
||||
Mockito.verifyZeroInteractions(this.brokerChannel);
|
||||
|
||||
Reference in New Issue
Block a user