Spring Session aims to provide a common infrastructure for managing sessions. This provides many Benefits including:
-
Accessing a session from any environment (i.e. web, messaging infrastructure, etc)
-
In a web environment
-
Support for clustering in a vendor neutral way
-
Pluggable strategy for determining the session id
-
Easily keep the HttpSession alive when a WebSocket is active
-
This section describes how to use Spring Session to use Redis when interacting with a web application’s HttpSession. If you’d like to skip the reading, you can also refer to the Sample
Before you use the project, you must ensure to update your dependencies. Instructions for building with Maven and Gradle have been provided below:
The project is available in the Spring Maven Repository. If you are using Maven, you will want to make the following updates.
Using the latest Milestone in Maven
If you want the latest milestone, ensure you have the following repository in your pom.xml:
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
Then ensure you have added the following dependencies:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.0.0.M1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.2</version>
</dependency>
Using the latest Snapshot in Maven
If you want the latest snapshot, ensure you have the following repository in your pom.xml:
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
Then ensure you have added the following dependencies:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
Using the latest milestone in Gradle
If you want the latest milestone, ensure you have the following repository in your build.gradle:
repositories {
maven { url 'https://repo.spring.io/libs-milestone' }
}
Then ensure you have added the following dependencies:
dependencies {
compile "org.springframework.session:spring-session:1.0.0.M1",
"org.springframework:spring-web:4.1.0.RELEASE",
"org.springframework.data:spring-data-redis:1.3.0.RELEASE",
"redis.clients:jedis:2.4.1",
"org.apache.commons:commons-pool2:2.2"
}
Using the latest Snapshot in Gradle
If you want the latest snapshot, ensure you have the following repository in your build.gradle:
repositories {
maven { url 'https://repo.spring.io/libs-snapshot' }
}
Then ensure you have added the following dependencies:
dependencies {
compile "org.springframework.session:spring-session-data-redis:1.0.0.BUILD-SNAPSHOT",
"org.springframework:spring-web:4.1.0.RELEASE"
}
Add the following Spring Configuration:
@Configuration
@EnableRedisHttpSession
public class Config {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
}
In our example, we are connecting to the default port (6379). For more information on configuring Spring Data Redis, refer to the reference documentation.
We next need to be sure our Servlet Container (i.e. Tomcat) is properly configured.
-
First we need ensure that our
Config
class from above was loaded. In the example below we do this by extendingAbstractHttpSessionApplicationInitializer
and passing ourConfig
class to the superclass. -
Next we need to be sure the
SessionRepositoryFilter
is regsitered with the Servlet Container. We can do this by mapping aDelegatingFilterProxy
to every request with the same name as the bean name of ourSessionRepositoryFilter
. Fortunately, this is performed automatically by theAbstractHttpSessionApplicationInitializer
.
public class Initializer extends AbstractHttpSessionApplicationInitializer {
public Initializer() {
super(Config.class);
}
}
The code contains a sample web application. To run the sample:
-
Obtain the source by cloning the repository or downloading it.
-
Run the application using gradle
-
Linux / OSX
./gradlew tomcatRun
-
Windows
.\gradlew.bat tomcatRun
-
-
Visit http://localhost:8080/
-
This can make clustering much easier. This is nice because the clustering setup is done in a vendor neutral way. Furthermore, in some environments (i.e. PaaS solutions) developers cannot modify the cluster settings easily.
-
We can use different strategies for determining the session id. This gives us at least a few benefits
-
Allowing for a single browser to have multiple simultaneous sessions in a transparent fashion. For example, many developers wish to allow a user to authenticate with multiple accounts and switch between them similar to how you can in gmail.
-
When using a REST API, the session can be specified using a header instead of the JSESSIONID cookie (which leaks implementation details to the client). Many would argue that session is bad in REST because it has state, but it is important to note that session is just a form of cache and used responsibly it will increase performance & security.
-
When a session id is acquired in a header, we can default CSRF protection to off. This is because if the session id is found in the header we know that it is impossible to be a CSRF attack since, unlike cookies, headers must be manually populated.
-
-
We can easily keep the HttpSession and WebSocket Session in sync. Imagine a web application like gmail where you can authenticate and either write emails (HTTP requests) or chat (WebSocket). In standard servlet environment there is no way to keep the HttpSession alive through the WebSocket so you must ping the server. With our own session strategy we can have the WebSocket messages automatically keep the HttpSession alive. We can also destroy both sessions at once easily.
-
We can provide hooks to allow users to invalidate sessions that should not be active. For example, if you look in the lower right of gmail you can see the last account activity and click "Details". This shows a listing of all the active sessions along with the IP address, location, and browser information for your account.
-
Users can look through this and determine if anything is suspicious (i.e. if their account has a session that is associated to a country they have never been) and invalidate that session and change their password.
-
Another useful example is perhaps they checked their mail at the library and forgot to log out. With this custom mechanism this is very possible.
-
-
Spring Security currently supports restricting the number of concurrent sessions each user can have. The implementation works, but does so passively since we cannot get a handle to the session from the session id. Specifically, each time a user requests a page we check to see if that session id is valid in a separate data store. If it is no longer valid, we invalidate the session. With this new mechanism we can invalidate the session from the session id.