Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support responding/sending messages to a session even without an authenticated user [SPR-11309] #15933

Closed
spring-issuemaster opened this Issue Jan 14, 2014 · 16 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Jan 14, 2014

Ilyes, Ben Hassen opened SPR-11309 and commented

The feature of message handling for a specific user with /user prefix was great. It allowed to simulate Request/Reply model without forcing the client to set extra "reply-to" headers and by using a generic destination name. This feature could, however, be extended to make Request/Reply for a specific Session without any Authentication requirement.

As with the /user prefix a client could subscribe to destination with a /session, e.g. /session/queue/a which will be handled by an extra SessionDestinationMessageHandler which will transform it into a destination unique to the session /session/queue/a-{sessionId}. On the Server side we could use it for example with the @SentToSession annotation to send the reply to /session/{sessionId}/queue/a which will be transformed by SessionDestinationMessageHandler to /queue/a-{sessionId} and than forwarded to the the Message Broker Handler who will notify the subscribed client.

In the attachment I have done a quick and dirty solution.


Affects: 4.0 GA

Attachments:

Referenced from: commits 97fb308

0 votes, 5 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 28, 2014

Rossen Stoyanchev commented

Thanks for the detailed suggestion.

User destinations however are not only request-reply. Any component can send a message to a user via SimpMessagingTemplate.convertAndSendToUser(user, destination, data). The key here is that a user's name is fixed and known. A session id is transient and not known. So sending to a specific session can only be supported in the context of a message being handled. That seems to be what you're looking for but we may be able to address the requirement without introducing a new kind of destination.

For example we could assign an anonymous Principal to the session when there is no authenticated principal. The name could be derived from the session id. That would make @SendToUser work even when a user is not logged in. Of course it still wouldn't be possible to send a message from anywhere but at least there is a way to send a response to a message that came from a specific session.

There is a 3rd option as well, where the application can assign the anonymous Principal information to a session, perhaps using a name that is more "fixed" or accessible from other parts of the application.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 28, 2014

Rossen Stoyanchev commented

Modified title (was: "Handling messages to websocket session destinations without authentication requirement")

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 29, 2014

Ilyes, Ben Hassen commented

Thank you very much for your interest.
Your suggestion works fine when the user stays always not connected, however if in the meanwhile he decides to login, the behaviour of @SendToUser changes because, according to the current implementation, the response will be sent to all sessions of the user which is not desirable in our case.
So there should be some distinction between sending to a user and to the current session of the user.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 30, 2014

Rossen Stoyanchev commented

If the user decides to log in, any messages sent to the actual user name will only reach the session of the authenticated user, not any other sessions they may have had prior to logging in which would be associated with a different (anonymous) identity.

In any case I would think that if this all happening in the same UI, the process of logging in should change the current page (hence ending the WebSocket session) and reloading the UI, hence re-establishing the WebSocket session with the authenticated user identity.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 8, 2014

Rossen Stoyanchev commented

This is now resolved and should be available in the next 4.1.0.BUILD-SNAPSHOT (requires repo.spring.io/snapshot).

Note that it was not necessary to introduce a new "/session" destination prefix. See the commit message here for details.

Ilyes, Ben Hassen, if you are able to give this a try that would be great.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 9, 2014

Ilyes, Ben Hassen commented

Thank you very much, I will try it out and give you feedback.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 30, 2014

Ilyes, Ben Hassen commented

Great!!!
It worked as expected. Many thanks for Rossen and all the Spring team.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented May 30, 2014

Rossen Stoyanchev commented

Alright, good to know, thanks for taking the time to test.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 2, 2014

Alex commented

I'm unable to make it work, may I have some help here?

For example if from server side I do a
messagingTemplate.convertAndSendToUser("0", "/queue/messages", "hello");
(with "0" being the sessionId assigned to the connected user)
on the client I've to subscribe to the queue /user/queue/messages ?

I tried this way and I don't receive anything.

Also, is it normale that sessionIds are simply integers counting from zero instead of being random uuids?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 2, 2014

Rossen Stoyanchev commented

You also need to have the sessionId in the headers the way it's done in SendToMethodReturnValueHandler. It's how the DefaultUserDestinationResolver realizes the user name is the session id.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 2, 2014

Alex commented

Ok, it works.
The missing bit was the headers, maybe that should be mentioned it in the documentation?
Thanks!

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Sep 2, 2014

Rossen Stoyanchev commented

I've updated the Javadoc of SimpMessageSendingOperations to explicitly mention this.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 9, 2018

jamesaq12wsx commented

I also cannot get works. Could I ask for help?

When client subscribe "/topic", I catch the subscribe event and use

messagingTemplate.convertAndSend(event.getMessage().getHeaders().get("simpDestination").toString()+"/queue/a-"+sessionID, responseStr, createHeaders(sessionID));

where createHeaders() is 

public MessageHeaders createHeaders(String sessionId) {
    SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(sessionId);
    headerAccessor.setLeaveMutable(true);
    return headerAccessor.getMessageHeaders();
}

But I still could not get the message at client side. where did i do wrong? I'vd find the solution for days.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 9, 2018

Rossen Stoyanchev commented

The subscribe event fires when the STOMP frame is received. However it may not have been processed by the broker yet. You can use an @SubscribeMapping controller method for returning initialization data to a client in request-response style.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 12, 2018

jamesaq12wsx commented

Hi Rossen Stoyanchev, I change my request-response to @SubscribeMapping. When I test, client subscribes "/chat" will first handle my SessionSubscribeEvent and then SubscribeMapping. 

In the configureMessageBroker in the config, I got 

registry.enableSimpleBroker("/chat", "/topic");

And in SubscribeMapping I got

messagingTemplate.convertAndSend("/chat", wrapMessage.toString());
messagingTemplate.convertAndSend(String.format("/chat/queue/a-{}", sessionID), wrapMessage.toString(), createHeaders(sessionID));

but in client I only get the one message from "/chat" destination.

Did I miss something? If I didn't fully speak of my situation, I got code at here.

Really thanks for ur answer.

 

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Nov 12, 2018

Rossen Stoyanchev commented

You missed a critical point because the @SubscribeMapping method returns void. Please pay close attention to the following:

"However for the return value, by default, a message is sent directly to the client (through clientOutboundChannel, in response to the subscription) and not to the broker (through brokerChannel, as a broadcast to matching subscriptions)".

 

Please do not post further replies here. This isn't the place for asking questions, especially on a closed unrelated ticket.  

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.