Search This Blog

Thursday, 5 December 2013

Listening for Session creation

With Spring security session creation and destruction is managed by the framework. Along with the session management code, we often tend to have some other activities performed. For e.g. I would like a count of the sessions created. As the code is now within Spring how do we add our audit code ? In a simple application the solution would be to add a HttpSessionListener.
Accordingly in my web.xml:
<listener>
  <listener-class>com.web.SessionCounter</listener-class>
</listener>
The code for the listener would be :
public class SessionCounter implements HttpSessionListener {
    private int counter = 0;

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        counter++;
        System.out.println("Total sessions created " + counter);
    }

    // other methods
}
Now whenever Spring creates a session, my listener is activated and the message will be logged to the console.
Total sessions created 1
The limitation with this approach is that the above listener is outside the Spring environment. If we need to access beans here, then the code gets complicated.
Spring Security provides a workaround for the problem. They have implemented a listener - HttpSessionEventPublisher.
public class HttpSessionEventPublisher implements HttpSessionListener {

    // ... code

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        HttpSessionCreatedEvent e = new HttpSessionCreatedEvent(
                event.getSession());
        Log log = LogFactory.getLog(LOGGER_NAME);
        if (log.isDebugEnabled()) {
            log.debug("Publishing event: " + e);
        }
        getContext(event.getSession().getServletContext()).publishEvent(e);
    }

    // other methods

}
As seen the Listener is similar to my listener in that both receive the HttpSessionEvent when a session is created anywhere within the application. The HttpSessionEventPublisher class however converts this event to a Spring event. It publishes the event to the ApplicationContext. Any bean that listens for this event will now be made aware of Session creation. The 2 steps to do the above would be:
  1. Update the deployment descriptor:
    <listener>
      <listener-class>
           org.springframework.security.web.session.HttpSessionEventPublisher
      </listener-class>
    </listener>
    
  2. Create the bean for the task:
    <beans:bean class="com.web.SessionCreatedListener"/>
    
The java code for the class is very much the same:
public class SessionCreatedListener implements
        ApplicationListener<HttpSessionCreatedEvent> {

    // as this is a bean, the listener code executes within the Spring
    // Security Framework

    private int counter = 0;

    @Override
    public void onApplicationEvent(
            HttpSessionCreatedEvent httpSessionCreatedEvent) {
        counter++;
        System.out.println("Total sessions created " + counter);

        Date timestamp = new Date(httpSessionCreatedEvent.getTimestamp());
        System.out.println("Session created at "
                + new SimpleDateFormat("yyyy-MM-dd").format(timestamp)
                + " and session is " + httpSessionCreatedEvent.getSession());
    }

}
If we run the code, the logs are as below:
DEBUG HttpSessionEventPublisher:66 - Publishing event: 
org.springframework.security.web.session.HttpSessionCreatedEvent
[source=org.apache.catalina.session.StandardSessionFacade@195f463]
TRACE XmlWebApplicationContext:332 - Publishing event in Root 
WebApplicationContext: org.springframework.security.web.session.HttpSessionCreatedEvent
[source=org.apache.catalina.session.StandardSessionFacade@195f463]
Total sessions created 2
Session created at 2013-08-12 and session is 
org.apache.catalina.session.StandardSessionFacade@195f463
Similarly for Session destroy, the beans need to listen for the HttpSessionDestroyedEvent. This one is also from the HttpSessionEventPublisher class (sessionDestroyed method). The HttpSessionEventPublisher also plays an important role in setting limits on the maximum number of concurrent sessions for a user.

1 comment: