2

I am implementing a Spring Boot + WebSocket + SockJS application and have a doubt about how to handle the HTTP session/ Websocket relation.

Basically I would like to be able to inform the user that his session has been invalidated (because of a timeout or because of having logged in from another location). For that I wanted to put in his own socket a certain message just before the framework closes it.

I turned to an http listener but the problem I have is that by the time HttpSessionListener.sessionDestroyed() is called I can see the socket has been already closed by the framework (not due to some other event like the user closing the browser).

Has anybody any idea about how to achieve this?

I have a really simple Websocket config and am running with Spring Boot defaults.

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setMessageSizeLimit(10000000);
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.setApplicationDestinationPrefixes("/app/");
    }

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

}

Security part:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        applySecurity(messages);
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }

    private static void applySecurity(MessageSecurityMetadataSourceRegistry messages) {

        messages.nullDestMatcher().authenticated() //
                .simpDestMatchers("/app/**").authenticated().simpSubscribeDestMatchers("/user/reply").authenticated()
                .simpTypeMatchers(MESSAGE, SUBSCRIBE).denyAll().anyMessage().denyAll();

    }
}  

My Http listener would be like this:

    return new HttpSessionListener() {

        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            simpMessagingTemplate.convertAndSendToUser(....);
        }

        @Override
        public void sessionCreated(HttpSessionEvent se) {
            // no need to do anything when a session is created
        }
    };

UPDATE:

Spring-session handles issues like this one and many others.

2 Answers2

2

HttpSession and Websocket session are two different kind of sessions. HttpSession is created when client accesses the url and completes Handshake whereas Websocket session is created when client sends subscribe request.

Websocket session is wrapped under HttpSession. If HttpSession gets timed out or invalidated then underlying Websocket session also gets disconnected, however, vice versa is not true.

Coming to your point of configuring listener for Websocket session, HttpSession listener won't be able to listen for Websocket session events. For such events, we need to define a class which extends org.springframework.web.socket.messaging.SubProtocolWebSocketHandler class and override afterConnectionEstablished and afterConnectionClosed methods. Have a look at Javadoc here.

Also, here is an example that listens for Spring websocket disconnect events. You need to configure something similar to this.

Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
  • Thank you for the information however I'm not sure we're talking about the same. I'm not interested in a listening websocket session events but http session events. You're right in the sense that the websocket session is bound to the http session. In that sense I'd like to send a message to the user though websocket like: "Hej, your http session is about to be invalidated so get ready..." After that message the http session will be invalidated and then the websocket will be closed but the user will know the reason. – Javier Moreno Garcia Mar 08 '16 at 01:22
  • I am not sure whether spring allows sending a message to a specific session, have a look at this SO answer (http://stackoverflow.com/questions/22367223/sending-message-to-specific-user-on-spring-websocket). Also, I don't think it's possible to get websocket session from httpsession unless we manually store session id mappings in a map as explained in that example. – Darshan Mehta Mar 08 '16 at 01:36
  • I have no problems in sending messages to users. The problem is that by the time HttpsessionListener.sessiondestroyed is called, the framework has already closed the web socket and I cannot use it anymore. The documentation says "receives notification that a session is about to be invalidated" which means the http session is still alive (and so should be the web socket session) however it is not true: the web socket connection is already closed. – Javier Moreno Garcia Mar 08 '16 at 01:48
  • The HTTP session being about to be invalidated does not mean that the web socket should still be alive. – user207421 Mar 08 '16 at 01:48
  • Well, both sessions are alive and when I invalidate the http session the websocket session seems to be closed before sessionDestroyed is invoked. Is there any way to hook something there? I just want to be able to put a "final" message in the web socket. If I'm not wrong it's Spring Security who closes the web socket (probably by means of a WebSocketHandlerDecoratorFactory) – Javier Moreno Garcia Mar 08 '16 at 01:56
  • As shown in this example (https://spring.io/blog/2014/09/16/preview-spring-security-websocket-support-sessions), you can store mapping of http session and corresponding websocket session somewhere in a class and then, use that to retrieve corresponding session to send a message. – Darshan Mehta Mar 08 '16 at 01:57
  • As indicated previously I have no problems with sending messages but with the timing. I'm looking to hook smth that is triggered when the session is about to be invalidated but before the web socket is closed. – Javier Moreno Garcia Mar 08 '16 at 02:00
0

An HTTP session is supposed to survive the connection. That's what it's for. Multiple connections, same session. The session should not expire while there are active connections.

user207421
  • 305,947
  • 44
  • 307
  • 483