This guide describes how to use Spring Session along with Spring Security using Hazelcast as your data store. It assumes you have already applied Spring Security to your application.
The completed guide can be found in the Hazelcast Spring Security sample application. |
1. Updating Dependencies
Before you use Spring Session, you must ensure to update your dependencies. If you are using Maven, ensure to add the following dependencies:
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
<version>3.9.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.10.RELEASE</version>
</dependency>
</dependencies>
2. Spring Configuration
After adding the required dependencies, we can create our Spring configuration.
The Spring configuration is responsible for creating a Servlet Filter that replaces the HttpSession
implementation with an implementation backed by Spring Session.
Add the following Spring Configuration:
@EnableHazelcastHttpSession (1)
@Configuration
public class HazelcastHttpSessionConfig {
@Bean
public HazelcastInstance hazelcastInstance() {
MapAttributeConfig attributeConfig = new MapAttributeConfig()
.setName(HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
.setExtractor(PrincipalNameExtractor.class.getName());
Config config = new Config();
config.getMapConfig(HazelcastSessionRepository.DEFAULT_SESSION_MAP_NAME) (2)
.addMapAttributeConfig(attributeConfig)
.addMapIndexConfig(new MapIndexConfig(
HazelcastSessionRepository.PRINCIPAL_NAME_ATTRIBUTE, false));
return Hazelcast.newHazelcastInstance(config); (3)
}
}
1 | The @EnableHazelcastHttpSession annotation creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter.
The filter is what is in charge of replacing the HttpSession implementation to be backed by Spring Session.
In this instance Spring Session is backed by Hazelcast. |
2 | In order to support retrieval of sessions by principal name index, appropriate ValueExtractor needs to be registered.
Spring Session provides PrincipalNameExtractor for this purpose. |
3 | We create a HazelcastInstance that connects Spring Session to Hazelcast.
By default, an embedded instance of Hazelcast is started and connected to by the application.
For more information on configuring Hazelcast, refer to the reference documentation. |
3. Servlet Container Initialization
Our Spring Configuration created a Spring Bean named springSessionRepositoryFilter
that implements Filter
.
The springSessionRepositoryFilter
bean is responsible for replacing the HttpSession
with a custom implementation that is backed by Spring Session.
In order for our Filter
to do its magic, Spring needs to load our SessionConfig
class.
Since our application is already loading Spring configuration using our SecurityInitializer
class, we can simply add our SessionConfig
class to it.
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityConfig.class, SessionConfig.class);
}
}
Last we need to ensure that our Servlet Container (i.e. Tomcat) uses our springSessionRepositoryFilter
for every request.
It is extremely important that Spring Session’s springSessionRepositoryFilter
is invoked before Spring Security’s springSecurityFilterChain
.
This ensures that the HttpSession
that Spring Security uses is backed by Spring Session.
Fortunately, Spring Session provides a utility class named AbstractHttpSessionApplicationInitializer
that makes this extremely easy.
You can find an example below:
public class Initializer extends AbstractHttpSessionApplicationInitializer {
}
The name of our class (Initializer) does not matter. What is important is that we extend AbstractHttpSessionApplicationInitializer .
|
By extending AbstractHttpSessionApplicationInitializer
we ensure that the Spring Bean by the name springSessionRepositoryFilter
is registered with our Servlet Container for every request before Spring Security’s springSecurityFilterChain
.
4. Hazelcast Spring Security Sample Application
4.1. Running the Sample Application
You can run the sample by obtaining the source code and invoking the following command:
Hazelcast will run in embedded mode with your application by default, but if you want to connect to a stand alone instance instead, you can configure it by following the instructions in the reference documentation. |
$ ./gradlew :spring-session-sample-javaconfig-hazelcast:tomcatRun
You should now be able to access the application at http://localhost:8080/
4.2. Exploring the security Sample Application
Try using the application. Enter the following to log in:
-
Username user
-
Password password
Now click the Login button.
You should now see a message indicating your are logged in with the user entered previously.
The user’s information is stored in Hazelcast rather than Tomcat’s HttpSession
implementation.
4.3. How does it work?
Instead of using Tomcat’s HttpSession
, we are actually persisting the values in Hazelcast.
Spring Session replaces the HttpSession
with an implementation that is backed by a Map
in Hazelcast.
When Spring Security’s SecurityContextPersistenceFilter
saves the SecurityContext
to the HttpSession
it is then persisted into Hazelcast.
4.4. Interact with the data store
If you like, you can remove the session using a Java client, one of the other clients, or the management center.
4.4.1. Using the console
For example, using the management center console after connecting to your Hazelcast node:
default> ns spring:session:sessions spring:session:sessions> m.clear
The Hazelcast documentation has instructions for the console. |
Alternatively, you can also delete the explicit key. Enter the following into the console ensuring to replace 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
with the value of your SESSION cookie:
spring:session:sessions> m.remove 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
Now visit the application at http://localhost:8080/ and observe that we are no longer authenticated.
4.4.2. Using the REST API
As described in the other clients section of the documentation, there is a REST API provided by the Hazelcast node(s).
For example, you could delete an individual key as follows (replacing 7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
with the value of your SESSION cookie):
$ curl -v -X DELETE http://localhost:xxxxx/hazelcast/rest/maps/spring:session:sessions/7e8383a4-082c-4ffe-a4bc-c40fd3363c5e
The port number of the Hazelcast node will be printed to the console on startup. Replace xxxxx above with the port number.
|
Now observe that you are no longer authenticated with this session.