How Spring Security Concurrent Session Control Works: Part 1
In this post, we will understand how a Spring Security concurrent session control works and how to customize it for a clustered environment.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
Spring security provides a mechanism to control and limit the maximum number of single-user open sessions. This mechanism prevents users from exceeding the number of allowed simultaneous connections. For example, Netflix limits the number of screens you can watch at the same time according to your subscription plan.
In part 1, we will understand how this mechanism works, how to use it, and being aware of the default spring security implementation limitations. In part 2, we will see how to overcome those limitations in a clustered environment.
How It Works
In this section, we will focus on the classes responsible for concurrent session management in Spring Security rather than authentication management and events publishing. So in order to simplify the explanation, all the authentication mechanism including the authentication manager, the authentication provider, the authentication event publisher... will be represented by the AuthenticationManager
.
- When a user authenticates successfully, the
AuthenticationManager
sends an event to theConcurrentSessionControlAuthenticationStrategy
. - The
ConcurrentSessionControlAuthenticationStrategy
requests the SessionRegistery for all the open sessions of the authenticated user. - The
ConcurrentSessionControlAuthenticationStrategy
checks if the number of open sessions, including the current one, exceeds the maximum number of allowed sessions. If not exceeded, the processing ends at this stage allowing the user to access the application. - If the limit is exceeded, the
ConcurrentSessionControlAuthenticationStrategy
checks the value of configuration parameterexceptionIfMaximumExceeded
:- If true, the
ConcurrentSessionControlAuthenticationStrategy
throws an exception preventing the user from login into the application. - If false, the
ConcurrentSessionControlAuthenticationStrategy
expires the oldest session of the authenticated user and allows him to access the application.
- If true, the
How to Use It
Project Preparation
If you are familiar with Spring Boot and Spring Security you can build your own test project, if not you can follow the steps described in this post: Spring Security 4 for Spring MVC Using Spring Data JPA and Spring Boot.
Concurrent Session Control Configuration
The code below shows how to configure the session control by creating two beans bean of types SessionRegisteryImpl
and ConcurrentSessionControlAuthenticationStrategy
respectively; and setting the maximum number of sessions to 3 and the parameter exceptionIfMaximumExceeded
value to true. So if a user authenticates more than three times he will be rejected.
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}
public SessionAuthenticationStrategy sessionAuthenticationStrategy(){
ConcurrentSessionControlAuthenticationStrategy sessionAuthenticationStrategy= new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
sessionAuthenticationStrategy.setMaximumSessions(3); // Set the max to 3
sessionAuthenticationStrategy.setExceptionIfMaximumExceeded(true);// throw exception if max exceeded
return sessionAuthenticationStrategy;
}
protected void configure(HttpSecurity http) throws Exception {
...
http
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy());
...
}
...
}
Limitations
The default implementation of SessionRegistery
uses volatile storage because it stores the session information in the memory. The code below shows a part of the class implementation where we can see that the session information is stored in a ConcurrentMap
. So if the server restarts that information will be lost and the user can authenticate again even if he exceeds the session limit.
xxxxxxxxxx
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<AbstractSessionEvent> {
...
// <principal:Object,SessionIdSet>
private final ConcurrentMap<Object, Set<String>> principals;
// <sessionId:Object,SessionInformation>
private final Map<String, SessionInformation> sessionIds;
...
}
Another limitation is related to the use of the default implementation in a cluster. Since there is no synchronization between the different cluster nodes, each node will manager the concurrent sessions separately. So the users can be authenticated in each node until reaching the limit. For example, if the maximum number of authorized sessions is M and we have N nodes, a user can authenticate successfully M times in each node so he can have MxN simultaneous open sessions.
Opinions expressed by DZone contributors are their own.
Comments