Guide to Spring Session

Spring Session provides an API and implementations for managing a user’s session information. In this post, we will cover features and advantages of using Spring Session in your Spring powered web application.

 

1. What is Spring Session?

Spring Session provides a transparent approach to resolve limitation of HTTP session. It provides central session management without being tied to container specific solution (e.g Tomcat, Jetty etc.).API provides integration with

  • HttpSession – Session API works by replacing the HttpSession with container netural way by providing session ID in headers.
  • WebSocket – Provides the ability to keep HttpSession alive with WebSocket messages.
  • WebSession – Replacing the Spring WebFlux’s WebSession in an application container neutral way.

On a high level, Session API provides following modules.

  • Spring Session Core.
  • Session Data Redis – Support for Redis based session management.
  • JDBC Session – Relation database based session support.
  •  Hazelcast – Support for Hazelcast.

Here are some of the benefits of using Spring Session API.

  • HttpSession – allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way.
  • Clustered Sessions – API makes it trivial to support clustered sessions without being tied to an application container specific solution.
  • Multiple Browser Sessions – Spring Session supports managing multiple users’ sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
  • RESTful APIs – Session API allows providing session ids in headers to work with RESTful APIs.

 

2. Spring Session with Spring Boot

Let’s create a simple Spring Boot web application to start with

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http:/x/www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.javadevjournal</groupId>
   <artifactId>spring-session-app</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>spring-session-app</name>
   <description>Demo project for Spring Boot</description>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.2.RELEASE</version>
      <relativePath />
      <!-- lookup parent from repository -->
   </parent>
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency> 
        <groupId>org.springframework.boot</groupId> 
        <artifactId>spring-boot-starter-security</artifactId> 
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
</project>

 

3. Spring Boot Configuration

Let’s configure our Spring Boot application for using Session API. We will add required dependencies using pom.xml file.

 

3.1 Maven Dependencies

Let’s add dependencies for Spring Session. We are adding a dependency for Redis as well which work as central storage for our session management.

<dependencies>
	<!-- ... -->
  <dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-session-data-redis</artifactId>
  </dependency>
</dependencies>

 

3.2 Spring Session Configurations

Spring Boot provides first class support for session API. Once we added required dependencies, we can enable Session support by setting StoreType property using application.properties file. We are using Redis for this post, to enable Redis for session API set following property 

spring.session.store-type=redis # Session store type.

Spring Boot support following store type for session API.

  • JDBC
  • Redis
  • Hazelcast
  • MongoDB

Based on the above property, Spring Boot will do a number of steps under the hood to enable Spring powered Session support.

  • Spring Boot create Spring Bean with name springSessionRepositoryFilter. This filter works under the hood to replace HttpSesion transparently with Spring backed session.
  • store-type property is equal to using @EnableRedisHttpSession annotation manually.

You can disable Spring Session by setting the store-type to none.

 

3.3 Redis Configurations

Spring Boot does a number of things to enable Redis support for the session management. It will automatically create a RedisConnectionFactory which connect Session API to Redis Server on localhost on port 6379. Use application.properties file to customize these configurations

spring.redis.host=localhost  #Server host
spring.redis.password=    #password
spring.redis.port=6379    #Redis server port

# Additional configurations 
server.servlet.session.timeout= # Session timeout.
spring.session.redis.flush-mode=on-save # Sessions flush mode.
spring.session.redis.namespace=spring:session # Namespace for keys used to store sessions.

This post assumes that you have already installed Redis server and it’s up and running.

 

4. REST Controller

Let’s create a simple REST control for our Spring Boot application.

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
public class SampleSessionRestController {

    /**
     * Simple Session controller which will return session ID backed by Spring Session API
     * @param session
     * @return session ID
     */
    @GetMapping("/")
    String uid(HttpSession session) {
        return session.getId();
    }

}

 

4.1.Spring Security Configuration 

Let’s do a basic setup to enable default configurations for Spring Security.

@Configuration
@EnableWebSecurity
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {

 @Autowired
 public void configureGlobal(AuthenticationManagerBuilder authentication) throws Exception {

  authentication.inMemoryAuthentication()
   .withUser("admin").password("nimda").roles("ADMIN");
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http
   .httpBasic().and()
   .authorizeRequests()
   .antMatchers("/").hasRole("ADMIN")
   .anyRequest().authenticated();
 }
}

 

5. How Does Spring Session Work?

API works transparently by replacing HTTP session. Instead of using Application Server (Tomcat etc.) HttpSession, it will persist value in the Redis server (or other store type defined in the application.properties).

Spring Session API will replace HttpSession value with the implementation that is backed by Redis. When Spring Security’s SecurityContextPersistenceFilter saves the SecurityContext to the HttpSession it is then persisted into Redis. When a new HttpSession is created, Spring Session creates a cookie named SESSION in your browser that contains the id of your session.

 

6. TEST Application

Let’s finally test our application to make sure Session API is working as expected.

public class SpringSessionAppApplicationTests {

 private TestRestTemplate testRestTemplate;
 private String testUrl = "http://localhost:8080/";


 @Test
 public void testUnauthenticated() {
  RestTemplate restTemplate = new RestTemplate();
  ResponseEntity < String > result = restTemplate.getForEntity(testUrl, String.class);
  assertEquals(HttpStatus.UNAUTHORIZED, result.getStatusCode());
 }

 @Test
 public void testSpringSessionAPI() {

  URI uri = URI.create(testUrl);
  RestTemplate restTemplate = new RestTemplate();
  ResponseEntity < String > firstResponse = firstRequest(restTemplate, uri);
  String sessionId1 = firstResponse.getBody();
  String cookie = firstResponse.getHeaders().getFirst("Set-Cookie");
  String sessionId2 = nextRequest(restTemplate, uri, cookie).getBody();
  assertThat(sessionId1).isEqualTo(sessionId2);

 }

 private ResponseEntity < String > firstRequest(RestTemplate restTemplate, URI uri) {
  HttpHeaders headers = new HttpHeaders();
  headers.set("Authorization", "Basic " + Base64.getEncoder().encodeToString("admin:nimda".getBytes()));
  RequestEntity < Object > request = new RequestEntity < > (headers, HttpMethod.GET, uri);
  return restTemplate.exchange(request, String.class);
 }

 private ResponseEntity < String > nextRequest(RestTemplate restTemplate, URI uri,
  String cookie) {
  HttpHeaders headers = new HttpHeaders();
  headers.set("Cookie", cookie);
  RequestEntity < Object > request = new RequestEntity < > (headers, HttpMethod.GET, uri);
  return restTemplate.exchange(request, String.class);
 }

}

Let’s see what we are trying to do with our unit test case.

  • With our first unit test case, we got unauthorized status back from the controller since we never passed default username and password in the request.
  • Our second unit test case divided into two parts
    • In the first request, we passed username and password to the controller and got success response back from the controller.
    • For the second request, we passed the same cookie in the request without passing username and password.
    • We compared the session id returned in both the request and found those equal.

To test it in more detail, you can also delete the explicit key in Redis. Enter the following into your terminal ensuring to replace session id with the value of your SESSION cookie

redis-cli del spring:session:sessions:session-id

If you try to visit your application, observe that we are no longer authenticated.

 

7. Spring Session without Spring Boot

In this section, we will quickly cover steps required to use Spring-managed Session in non Spring Boot application.

 

7.1 Dependencies

Before using session API, we need to make sure to update dependencies

<?xml version="1.0" encoding="UTF-8"?>
<dependencies>
   <!-- ... -->
   <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>{spring-session-version}}</version>
      <type>pom</type>
   </dependency>
   <!-- se Lettuce with Spring to manage the RedisClient and the RedisClusterClient -->
   <dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
      <version>5.0.4.RELEASE</version>
   </dependency>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>{spring-version}</version>
   </dependency>
</dependencies>

 

7.2 Java Configuration

@EnableRedisHttpSession
public class ApplicationConfig {

 @Bean
 public LettuceConnectionFactory connectionFactory() {
  return new LettuceConnectionFactory();
 }
}
  • The @EnableRedisHttpSession annotation creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter.
  • RedisConnectionFactory connects Session API to the Redis Server.

 

7.3 Java Servlet Initialization

public class ServletContainerInitializer extends AbstractHttpSessionApplicationInitializer {
 public Initializer() {
  super(ApplicationConfig.class);
 }
}

Let’s take a look at few important points

  • We extended our ServletContainerInitializer from AbstractHttpSessionApplicationInitializer to make sure that we have correct spring bean available (springSessionRepositoryFilter)
  • AbstractHttpSessionApplicationInitializer also provides a mechanism to easily make sure Spring loads our ApplicationConfig.

 

Summary

In this post, we covered different features and use cases for Spring Session API. This API provides a transparent way to handle session for our application which provides flexibility and power to wire up multiple applications to the same Redis instance and share authentication information.

2
Leave a Reply

avatar
1 Comment threads
1 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Umesh AwasthiAtul More Recent comment authors

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
newest oldest most voted
Notify of
Atul More
Guest
Atul More

Very nice explanation.
Keep posting.

Umesh Awasthi
Admin
Umesh Awasthi

Happy that you liked this post. 🙂