Getting Started with Spring Session

27.09.2017 by Jens in Spring Boot

In this tutorial, we are going to look at Spring Session and build two applications which share the session in Redis.

Spring Session provides a mechanism for managing user’s session information in an application container independent way. In its essence, it will replace the container stored HttpSession with its own implementation. This implementation relies on various stores, like Redis or JDBC, to actually store the user’s session information. As it is not tied to a particular application container or application anymore, you basically get a clustered session store out of the box.

Furthermore, it provides a ServletFilter for session handling via HTTP headers too so we can use it even in RESTful APIs.

When we use Spring Session, the default JSESSIONID cookie is replaced with one named SESSION.

The Example Application(s)

You can find the full source code on GitHub.

We have two applications in this example. First, a simple UI and second a REST API. They share the same session store. You obtain the session id in the UI and can use it in the REST API. We do it manually with curl; however, in the real world, you’d probably do it in Angular or whatever SPA framework you are using.

Both use the same starter dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

The UI

The UI consists of single HTML page which is protected by a form-based login.

The application:

@SpringBootApplication
@EnableWebSecurity
public class SessionUiApplication {

    public static void main(String[] args) {
        SpringApplication.run(SessionUiApplication.class, args);
    }
}

The security config:

@Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .formLogin()
                .and()
                    .authorizeRequests()
                    .anyRequest()
                    .authenticated();
    }

}

We disable csrf for testing purposes and protect all resources with a form-based login.

The index.html page:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Tutorial UI</title>
    </head>
    <body>
        I am logged in.
    </body>
</html>

The API

Provides a simple /name endpoint which plainly returns the logged in user’s name.

@SpringBootApplication
@EnableWebSecurity
@RestController
public class SessionApiApplication {

    @GetMapping("/name")
    public String getName(Principal principal) {
        return principal.getName();
    }

    public static void main(String[] args) {
        SpringApplication.run(SessionApiApplication.class, args);
    }
}

And its security config:

@Configuration
public class WebSecurity extends WebSecurityConfigurerAdapter{

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
    }

}

The User

We use the default in-memory user store with the default user named user. The only thing we change is we set a fixed password for the user in application.properties like:

security.user.password=password

Setting Up Spring Session

The first thing, we need to do for using Spring Session in Spring Boot is to add its starter:

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session</artifactId>
</dependency>

Now the auto-configuration will try to set up Spring Session but will fail in an essential part. It must know which backend store we want to use. We can either declare it with various annotations, e.g., @EnableRedisHttpSession or, as we will use, set a single property in application.properties like:

spring.session.store-type=redis

Now, the auto-configuration will now use a Redis for the Session and set up everything accordingly. We already added the spring-boot-starter-data-redis before. Redis can be configured with all the ways Spring Data Redis provides.

Start the UI application now and let’s login with curl.

curl -X POST -F "username=user" -F "password=password" -v http://localhost:8080/login

And in the response there is the SESSION cookie value like:

< Set-Cookie: SESSION=3f749733-f384-47ca-a351-fc71595583f0; Path=/; HttpOnly

We are now identified by the value of the SESSION cookie. When our applications run all on the same domain, we can just authenticate with the cookie. However, in a RESTful API, you usually, don’t want to log in via a cookie.

For RESTful APIs

For our REST API, we must also do the above steps plus a few additional ones. So, let’s focus on the new ones:

First, in our API we do not want a new session created on each request, so we disable session creation in application.properties.

security.sessions=never

It will use an existing session but will not create a new one.

Next, we must define where Spring Session looks for the session id. It uses a HttpSessionStrategy for this and by default, uses the CookieHttpSessionStrategy which will work with the session cookie. To change it, we override it by declaring a different strategy in our @Configuration class, i.e. SessionApiApplication:

@Bean
public HttpSessionStrategy httpSessionStrategy() {
    return new HeaderHttpSessionStrategy(); 
}

HeaderHttpSessionStrategy does two things. First, it expects the session id in the HTTP header x-auth-token and uses it for identification. Second, it will add the same header to each response so we can extract it there.

When we start the API now and run curl:

curl http://localhost:8081/name -v -H "X-Auth-Token: 3f749733-f384-47ca-a351-fc71595583f0"

We’ll get the username as a response:

user

and the header in the response:

> X-Auth-Token: 3f749733-f384-47ca-a351-fc71595583f0

Conclusion

Spring Session can be a good choice when you need a shared session state. It is easy to set up and covers standard use cases.


comments powered by Disqus