Redis with spring session

How session works with redis?

Instead of Tomcat’s Embedded HttpSession stored in memory, Spring-Session reimplements HttpSesson with Redis to support multiple sessions with same browser.

Try it up with debugger

Let's first create a demo project, go to starter page and create a demo project with following plugin:

  • Redis
  • Web
  • Session

Add @EnableRedisHttpSession to BootApplication

@SpringBootApplication
+ @EnableRedisHttpSession
public class DemoApplication {
	...
}

And create a Hello Controller for debugging

@RestController
@RequestMapping("/")
public class Hello {

  @GetMapping("hello")
  public String hello(HttpServletRequest request, @RequestParam String value){
    // add debugger here
    request.getSession().setAttribute("key",value);
    return value;
  }
}

Add debugger with find usage for getter/setter

org.springframework.session.web.http.SessionRepositoryFilter#CURRENT_SESSION_ATTR

Cookies resolver

org.springframework.session.web.http.CookieHttpSessionIdResolver#resolveSessionIds

And these are the mapping between cookies and redis

org.springframework.session.web.http.DefaultCookieSerializer#readCookieValues
org.springframework.session.web.http.DefaultCookieSerializer#writeCookieValue

then start a local redis(localhost:6379) and run springboot in debug mode.

Let's try with a curl

 curl "http://localhost:8080/hello?value=1"

Finally, login to redis-cli, you will find sessions are already in Redis.

KEYS spring:session:sessions:*

How to reuse session-id?

By default, session ids are generated by UUID, they have no relation with browser or IP address.

org.springframework.session.MapSession#generateId

To reuse the session-id, every request from browser must have a valid session in the header(cookies or x-auth-token), I prefer header based session resolver (less payload and easier for non-browser clients).

@Bean
HttpSessionIdResolver headerResolver(){
	return new HeaderHttpSessionIdResolver("miao-token");
}

Recurl and your will get a miao-token header in response.

Content-Type: ...
Date: ...
miao-token: f15b28ea-15b6-4ce8-aad5-9ed0ab00d643

To keep the client's session up to date with server, just add the header in HttpInterceptor(eg: okhttp/ajax).

// angular HttpInterceptor, same as okhttp
export class AddHeaderInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const clonedRequest = req.clone({ 
    	headers: req.headers.set('miao-token', YOUR_GLOBAL_TOKEN) 
		});
    return next.handle(clonedRequest);
  }
}

In the end, here is the resuable process flow.

RESTful Request
	|
HttpSessionIdResolver
	|
(has token id)?
	|  
	|-------(yes): get seesion by id from redis and reuse.
	---------(no): jump to login page and create id by UUID

Appendix

What's cookie's scope?

See Scope of cookies, if you share two applications with same session, you need to customize the DefaultCookieSerializer for wider scope(path/httpOnly attributes).

CSRF protection

CSRF(Cross-Site Request Forgery) attacks may inject malicious code into webpages , see more at Spring-Securiy for server-side protection and Angular for client-side protection.

Alternatives

If you prefer a lower level redis store implementation, just try tomcat-redis-session-manager

com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve

SSO

You can also use CAS, a SSO solution for your authentication and authorization, which have a redis plugin inside.