Spring Security - Spring Session Redis

Spring Session Redis Demo

By Default Spring boot stores user session info in Server’s memory. If we have more than one instance of web application behind a load balancer, this will cause problem because the request has to be route to the same instance to retrieve session data. One solution is to use sticky session. Sticky session has many disadvantages. One of the disadvantage is servers may go down, the session data is lost.

Spring Session uses data store to store session data. So we can implement a stateless application. This is valuable in the cloud environment.

Spring Session supports many data store

  • Redis
  • JDBC
  • MongoDB
  • GemFire

The most popular store is Redis.

Start Redis Server

To make it simple, we can start redis server as Docker container

1
docker run -d --rm -p 6379:6379 --name redis-demo redis

You can get into the container and execute redis-cli to bring up the Redis CLI to interact with the Redis Contaienr

Maven Dependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</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-web</artifactId>
</dependency>

Spring Session Configuration

We need to set the redis server connection and Spring Session’s store type in application.properties

1
2
3
4
5
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=

spring.session.store-type=redis

We also need to enable Redis session using @EnableRedisHttpSession annotation

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableRedisHttpSession
public class SpringSessionRedisApplication {

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

}

That is all we need for Spring Session Redis configuration.

Security Config

We can define a regular user and admin user in Security Configuration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and()
.authorizeRequests()
.mvcMatchers("/").hasRole("USER")
.anyRequest().authenticated();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.and()
.withUser("admin")
.password(passwordEncoder().encode("password"))
.roles("ADMIN");
}
}

Controller

This controller providers 3 endpoints. /setValue sets session attribute “foo” and /getValue gets “foo’ attribute value from session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RestController
public class HomeController {
private static final Logger LOG = LoggerFactory.getLogger(HomeController.class);
@GetMapping("/")
public String home() {
return "Home";
}

@GetMapping("/setValue/{val}")
public String setValue(HttpServletRequest request, @PathVariable String val) {
HttpSession session = request.getSession();
session.setAttribute("foo", val);
return "OK";
}

@GetMapping("/getValue")
public String getValue(HttpServletRequest request) {
HttpSession session = request.getSession();
if( session.getAttribute("foo") != null){
return session.getAttribute("foo").toString();
}
return "Can't find Value";
}
}

Test

We can login and set value using User and Admin account. Spring will store the session in Redis.

Use KEYS command to check the keys set in redis:

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> keys *
1) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:admin"
2) "spring:session:sessions:756cb785-5b04-43e3-99cf-0f82bb9d0767"
3) "spring:session:sessions:e4429c73-4d26-41d6-b598-b7c9b0dc2116"
4) "spring:session:expirations:1592599800000"
5) "spring:session:expirations:1592599860000"
6) "spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:user"
7) "spring:session:sessions:expires:dfb4e97c-681b-4710-8bd7-667f95621f0c"
8) "customer:-5656296892928562510"
9) "spring:session:sessions:dfb4e97c-681b-4710-8bd7-667f95621f0c"
10) "spring:session:sessions:expires:756cb785-5b04-43e3-99cf-0f82bb9d0767"

You can view the source code from Github: https://github.com/xinghua24/SpringBootExamples/tree/master/SpringSessionRedis

Reference