Spring Security and Session with Redis
There was this product I was working on with my team where we wanted to achieve a multi-instance production environment. This would bring us several benefits like zero-downtime deploys, greater availability assurance and scalability in order to attend high demand peaks.
We had two main problems to solve in order to achieve this: store users’ session information outside the application container and externalize the websockets host.
This is the first part on this report on how to externalize application container state where we solve the first of the two problems. The source for this demo is available on Github.
Scenario
The product in this matter is a web application built on Spring Framework and a few of its sub-projects: Data and Security.
Problem
Users’ session information were currently kept in-memory by our application container, which was Wildfly. As it seems, it is a problem when running on a multi-instance environment to have sessions stored in every server. That may cause unexpected application behavior when one client request hits server A but a second gets handled by server B, where this user’s session does not exist.
Another problem would be when server A goes down and from then on, server B has to handle all the load. That would occur ok if all sessions on server A were not lost.
Solution
Store all sessions in the same place, where they would be available and manageable for all running application container instances. This way when one server goes down the others know where to find session information that the dead server was working with.
Spring framework has one project under its umbrella which provides integration with Redis, which in summary is a in-memory key-value database. This sub-project is called Spring Session and implements several features, one among them is abstracting HttpSession
to not depend on application container specific solution.
Spring Session with Redis
The first step is to add Spring Session Data Redis and Lettuce dependencies:
Having the dependencies ok, it is time to configure the connection factory and enable Spring Session with Redis. This is done by the following config class:
@EnableRedisHttpSession will allow HttpSession
to be backed by Spring Session and therefore stored in Redis.
The LettuceConnectionFactory
bean will be the connection factory Spring Session requires. It is in here that host and port configuration for Redis can be specified. Default values for these are localhost
and 6379
respectively.
This is all what it takes to have Spring Session enabled and integrated to Redis.
Spring Security
In order to demo user session stored and retrieved from Redis, let’s configure Spring Security. First of all, we add Spring Security dependency:
Having that done we configure Spring Security through the following class:
configure
is simply making sure that any request has a user authenticated.
configureGlobal
defines an in-memory authentication with a admin/admin dumb user.
That’s it on the configuration part. Now, let’s play and see it working.
Managing Sessions
The following controller defines an endpoint that counts page views per session and stores the result in the session itself:
And this is its respective Thymeleaf template:
This template renders the number of page views which is populated in our controller. It also presents a form that allows users to logout.
Playing the result
Start our demo application and go to localhost:8080
. Since we configured Spring Security to make sure any request is authenticated, the first page to be presented is login
:
Log in as admin/admin. IndexController
will be called and the session attribute pageViews
is created and populated in the user session:
Every time you refresh this page the pageViews
session attribute gets incremented and updated in the session which in turn is stored in Redis. If user logs out and log in again a new session is created and the counter will start over.
Now the true gain we have with this environment is that if we stop our application and start again user session will be intact. When the user hits refresh he will still be logged in and as one would expect, his pageViews
attribute will continue to be incremented from where it was before the application was restarted.
Conclusion
In this article we saw how to configure Spring Session with Redis and have Spring Security integrated in this environment. All this allowed us to store users’ sessions in Redis. Therefore, they are not lost when an application container goes down.
Another benefit is that when using a centralized session storage it does not matter which application container handles a client request because any of them have access to session information.
I hope it helps and feedback is always welcome! :)
Post by: Djeison Aguir Selzlein