Screenshot from 2014-08-23 20:44:23This post introduces you to Spring Session, by showing you how easy it is to replicate sessions in a vendor neutral way and how they can be used across different protocols and technologies.

Motivation

When it comes to web application development, user session handling is as easy as obtaining an HTTP session from a request. So why should you care about a session abstraction as the one provided by Spring. Actually, there are a couple of reasons, so I will just name a few of them:

  1. Did you ever try to configure HTTP session replication, so your application becomes more reliable with respect to fail-over? If not, have a look at the according Tomcat manual, for example, so you get an impression of what this may look like in production grade environments. Moreover, you may even not be able to configure proper session replication at all, if you are planning to deploy your application to the cloud, since cloud providers may not let you get your hands on any of the necessary components, e.g. the load balancer configuration.
  2. Secondly, session handling shouldn’t actually be technology dependent. Unfortunately, as with many other Java standards, combining technology with business logic has always been a common practice. As such, web applications strongly stick to HTTP sessions without even thinking about technology agnostic alternatives. This makes it nearly impossible to get hold on an HTTP session from within a JMS message context or a web socket, although the latter one, as the name suggests, is closely related to web applications.
  3. Furthermore, session lifecycle management and storage, especially with HTTP, has always been transparent to the application developer, which is fine in general. However, often it is inevitable to have more fine-grained control over the sessions within a running application. e.g. in order to invalidate sessions or to limit the number of simultaneous sessions for a single user. This is often achieved through workarounds, such as tracking sessions with the help of SessionEventListeners. However, all these approaches are neither suitable for a clustered environment nor are they scalable, at all.

Introducing Spring session abstraction

Luckily, the solution to all these problems is far more simple than it seems: Session management has to become part of the application itself, getting rid of the servlet container or application server in that case. What sounds weird in the beginning, is the key to success, when it comes to explicit session management as part of the application code. With Spring Session the following is all we need for a complete drop-in replacement of the standard HTTP session management provided by today’s servlet containers:

  1.  A key-value store is required for storing sessions and their contained data. Spring session provides an in-memory map store for testing purposes and a Redis integration for production use. The latter one provides automatic session replication as a side effect.
  2. A session retrieval strategy is required in order to associate HTTP requests with existing sessions from the data store. Both cookie-based lookups, as known from standard HTTP sessions, as well as HTTP header based lookups are supported by default.
  3. Finally, a standard servlet filter is required, which applies the session strategy to incoming requests and wraps the session in a standard HttpSession. This is achieved through a servlet filter using a standard HttpServletRequestWrapper for providing the HttpSession to the servlet code, if requested. This effectively bridges the web application code with the back-end data store.

Integrating HTTP session management with Redis

In this section I will show you, how to integrate HTTP session handling with a Redis data store. For the sake of simplicity, I will stick with an embedded Redis server instance, which, of course, does not replicate across multiple JVMs, automatically. First of all, Spring Boot is used to build a stand-alone web application based on the following Maven POM:

A simple web MVC controller is used to demonstrate access to HTTP sessions, retrieving their id and state, as follows:

The controller is bound to two separate URLs relative to the application context, namely /ui/session and /cli/session. This will be used later to apply different HTTP session retrieval strategies. Together with the following HTML view, the application will simply output the session id and its state, when accessed by means of any regular HTTP client:

So far, this code will print HTTP sessions generated by the web container, which in case of Spring Boot could be an embedded Tomcat. For the integration with a Redis key-value store, more configuration is required. For testing purposes an “embedded” Redis server is used, which is provided by a Spring Java configuration, as follows:

The integration with the Redis server is then achieved by the following Spring XML configuration:

The configurations provides the following beans:

  1. A RedisTemplate for accessing the previously configured RedisServer. The appropriate connection factory is automatically provided by Spring Boot based on RedisAutoConfiguration.
  2. A Redis-based session repository for dealing with the actual session objects. The repository provides typical CRUD operations, which then delegate to the Redis data store. In my case, there is also a configured session expiration based on a new property, which you must add to your application.properties. I have set it to 10 seconds, so sessions quickly expire within the Redis store. This, of course, is just for demo purposes.
  3. A SessionRepositoryFilter, which is applied to /ui/* requests. This filter, by default extracts and stores sessions using cookie-based HTTP session strategy. This is almost equivalent to the standard behavior of the web container, although, by default, the cookie itself is named SESSION, not JSESSIONID.

Finally, a Spring Boot application class is used to tie everything together and enable us to start the whole web application:

You can now access the session controller at http://localhost:8080/ui/session. You may notice that the session id format has changed, since Redis uses UUID format, while Tomcat produces strings without any dashes. You may check the differences, by accessing the same controller via http://localhost:8080/cli/session, which so far isn’t yet integrated with Spring’s session abstraction. Also note, how the sessions expire, if you do not refresh the page within the specified session expiration interval.

Screenshot from 2014-08-23 18:51:45

Spring Session Controller in action

Using different HTTP session strategies

In this section I am going to assume, that the application shall be accessed by some kind of command line interface, e.g. via REST. That’s why I previously added the /cli mapping to the SessionController, though, in real life there would be separate controllers for this. The idea is to be capable of accessing the very same session both via cookie, as for the UI, and via HTTP request header token in the case of the CLI. The previously configured SessionRepositoryFilter provides the means to configure an HTTP session strategy explicitly, so a second filter simply has to be added to the XML configuration, as follows:

 Note, that both filter registrations have an explicit name configured. This is necessary, so that both get registered. Otherwise Spring will deduce a name for each filter registration automatically, which in both cases boils down to delegatingFilterProxy, thus, ignoring the second registration silently.

The HeaderHttpSessionStrategy used for the second filter registration uses an HTTP header attribute named x-auth-token, which may be configured differently, if required. It is now possible to access (or obtain) the HTTP sessions both via cookie, using the /ui mapping, and via header using the, /cli mapping, also interchangeably. The latter one is best suited for command line usage, e.g. as follows:

This is a powerful approach, when using REST services repeatedly with proper authentication. The first request is simply issued using HTTP basic authentication, while all further requests use the authentication token returned in the first response, which simply refers to the HTTP session, usually holding the security context. Moreover, REST controllers may also cache frequently used data within the session, if necessary.

 Accessing the session repository directly

The observant reader may have noticed, that I annotated the Spring Boot application class using @EnableScheduling, which wasn’t used so far. In this section I want to show you how to print some session statistics from within the application. Therefore, I am going to access the session repository directly, i.e. in order to obtain the last access time of each active session. Unfortunately, the session repository, for obvious reasons, does not allow bulk access to all session, but rather requires you to provide a suitable id. Therefore, I am going to abuse the RedisTemplate to get a hold on all the sessions in the Redis store. The following class simply needs to be added to the application package to log the session statistics using SLF4J:

Although this should not be used in production grade software for security reasons, it shows that all the data stored within the HttpSession is also directly accessible through the Session obtainable from the SessionRepository. In the end, the SessionRepositoryFilter simply wraps the Session object with an HttpSession, so that it can be obtained from the servlet request.

Summary

Although, at the time of writing Spring session wasn’t even available as release candidate, it is more than obvious that it will drastically simplify session management in the future. Furthermore, it is actually a spin-off project from Spring Security, which I assume is going to integrate it tightly. This leads me to conclude that session abstraction is going to be standard, if one relies on Spring Security. As such, both authentication and session abstraction will be available across transport layers and technologies such as Web Sockets and messaging, while sessions are automatically replicated via the Redis store.

This download contains the sample project used within this blog entry.

Leave a Comment

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close