Introduction:
Spring Security allows us to manage HTTP sessions effectively. This control extends from session timeouts to enabling concurrent sessions and other advanced security configurations. In Spring Security, sessions are created and managed by the SessionAuthenticationStrategy
interface. This interface defines the contract for storing and retrieving the authenticated principal (user) in the session. Spring Security provides several implementations of this interface, each with its own characteristics and use cases.
When is the Session Created in Spring Security?
In Spring Security, a session is typically created when a user successfully authenticates with the application. The exact timing of session creation depends on the authentication mechanism used, such as form-based authentication, basic authentication, or token-based authentication.
For example, in the case of form-based authentication, the session is created after the user submits their credentials and successfully authenticates. The authenticated principal (user) is then stored in the session, allowing subsequent requests to identify and authorize the user based on the session information. We can control exactly when our session gets created and how Spring Security interacts with it. Here are the options:
- always: A session will always be created if one doesn’t already exist.
- ifRequired (default): A session will be created only if required.
- never: Spring Security won’t create a session itself, but it will use one if it already exists.
- stateless: No session will be created or used by Spring Security.
Under the Hood
Spring Security relies on the servlet container’s session management capabilities to handle sessions. When a session is created, Spring Security stores the authenticated principal (user) in the session using the configured SessionAuthenticationStrategy
.
The default SessionAuthenticationStrategy
implementation is CompositeSessionAuthenticationStrategy
, which combines multiple strategies. One of the strategies included by default is RegisterSessionAuthenticationStrategy
, which associates the currently authenticated principal with the session.
Before running the authentication process, Spring Security stores the Security Context between requests. It also provides filters responsible for session management, such as SessionManagementFilter
and HttpSessionSecurityContextRepository
.
These stricter control mechanisms have implications:
- Cookies are not used, so each request requires re-authentication.
- Stateless architectures work well with REST APIs and authentication mechanisms like Basic and Digest Authentication.
1. Concurrent Session Control
Spring Security provides mechanisms to control and manage concurrent sessions for the same user. This feature is useful in scenarios where you want to prevent a user from being logged in from multiple locations simultaneously or to enforce a single sign-on (SSO) policy.
To enable concurrent session control, you can use the sessionManagement()
method in the HttpSecurity
configuration:
package com.jbd.springsecuritytutorials;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
.headers(headers -> headers
.httpStrictTransportSecurity(Customizer.withDefaults())
)
.sessionManagement(session -> session
.sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.enableSessionUrlRewriting(false)
);
return http.build();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
In this example, we set the maximumSessions
to 1
, which means that only one session is allowed for a user at a time. If a user tries to log in from a different location, the previous session will be invalidated. The maxSessionsPreventsLogin
option determines whether the new login attempt should be allowed or denied when the maximum number of sessions is reached.
2. Session Timeout
Spring Security allows you to configure a session timeout, which automatically invalidates the session after a specified period of inactivity. This helps mitigate security risks associated with unattended sessions and enhances the overall security of your application.
2.1 Handling the Session Timeout
When a session times out, Spring Security triggers a SessionAuthenticationException
. You can handle this exception by configuring an AuthenticationEntryPoint
or by implementing a custom exception handling mechanism.
Here’s an example of handling the session timeout by redirecting the user to the login page:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.invalidSessionUrl("/session-expired")
).successHandler(new CustomAuthenticationSuccessHandler())
return http.build();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException, ServletException {
request.getSession().setMaxInactiveInterval(1800); // Set timeout to 30 minutes (in seconds)
super.onAuthenticationSuccess(request, response, authentication);
}
}
2.2 Configure Session Timeout with Spring Boot
If you’re using Spring Boot, you can configure the session timeout using the server.servlet.session.timeout
property in your application.properties
or application.yml
file:
# application.properties
server.servlet.session.timeout=30m
# application.yml
server:
servlet:
session:
timeout: 30m
In this example, the session timeout is set to 30 minutes. If no requests are received within this time period, the session will be invalidated.
3. Prevent URL Parameter for Session Tracking – Session Fixation
By default, Spring Security uses cookies for session tracking. However, it is possible to enable URL rewriting for session tracking, which can be a security risk as it exposes the session ID in the URL.
To prevent URL rewriting for session tracking, you can configure Spring Security with the sessionFixation().migrateSession()
option:
@Configuration
@EnableWebSecurity
public class SecurityConfig2 {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionFixation().migrateSession()
.invalidSessionUrl("/session-expired")
.maximumSessions(1)
);
return http.build();
}
//other stuffs
}
This configuration ensures that Spring Security uses cookies for session tracking and prevents URL rewriting, enhancing the security of your application.
3.1 Understanding Session Fixation:
In Spring Security 6.2, session fixation attacks are mitigated by default. However, you can still configure it explicitly for better visibility and control. You can control the strategy for Session Fixation Protection by choosing between three recommended options:
changeSessionId
– Do not create a new session. Instead, use the session fixation protection provided by the Servlet container (HttpServletRequest#changeSessionId()
). This option is only available in Servlet 3.1 (Java EE 7) and newer containers. Specifying it in older containers will result in an exception. This is the default in Servlet 3.1 and newer containers.newSession
– Create a new “clean” session, without copying the existing session data (Spring Security-related attributes will still be copied).migrateSession
– Create a new session and copy all existing session attributes to the new session. This is the default in Servlet 3.0 or older containers.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.sessionManagement((session) -> session
.sessionFixation((sessionFixation) -> sessionFixation
.newSession()
)
);
return http.build();
}
4. Secure Session Cookie
In Spring Security 6.2, you can secure cookies by configuring the CookieSpec
for various cookies used by the framework. Here’s an example of how to secure the session cookie and other security-related cookies in your Security
configuration. I am using the Spring-Session-library.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
//other stuffs
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setUseSecureCookie(true);
serializer.setSameSite("Strict"); // default is Lax
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
}
httpOnly(true)
: This setting prevents client-side scripts (e.g., JavaScript) from accessing the session cookie, mitigating the risk of cross-site scripting (XSS) attacks.secure(true)
: This setting ensures that the session cookie is only transmitted over HTTPS connections, preventing potential eavesdropping or man-in-the-middle attacks on insecure (HTTP) connections.maxAge(3600)
: This setting specifies the maximum age of the session cookie in seconds. In this example, the cookie will expire after one hour (3600 seconds) of inactivity.sameSite(SameSiteFilter.SameSiteValue.STRICT)
: This setting configures the SameSite attribute of the cookie, which helps mitigate cross-site request forgery (CSRF) attacks. TheSTRICT
value ensures that the cookie is only sent for same-site requests, providing an additional layer of security.
These configurations enhance the security of the session cookie by limiting its accessibility, ensuring secure transmission, setting an expiration time, and preventing potential cross-site attacks.
Working With the Session
Spring Security provides several ways to work with the session in your application, such as injecting session-scoped beans, accessing the raw session, and obtaining session information.
Session Scoped Beans
Spring Security supports session-scoped beans, which allow you to store and retrieve data associated with the current user’s session. This can be useful for maintaining user-specific state or data across multiple requests.
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;
@Component
@SessionScope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionData {
private String data;
// Getters and setters
}
In this example, the SessionData
bean is session-scoped, and Spring Security will create a new instance for each user session. You can inject this bean into your controllers or services and access or modify the session data as needed.
Injecting the Raw Session Into a Controller
Spring Security allows you to inject the raw HttpSession
object into your controller methods, giving you direct access to the session data and attributes.
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SessionController {
@GetMapping("/session-data")
public String getSessionData(HttpSession session) {
String sessionData = (String) session.getAttribute("sessionData");
// Process the session data
return sessionData;
}
}
In this example, the HttpSession
object is injected into the getSessionData
method, allowing you to retrieve and manipulate session attributes as needed.
Obtaining the Raw Session
If you need to access the raw HttpSession
object outside of a controller method, you can obtain it from the SessionRegistry
in Spring Security.
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.session.SessionRegistry;
import org.springframework.stereotype.Service;
@Service
public class SessionService {
@Autowired
private SessionRegistry sessionRegistry;
public HttpSession getSession() {
UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return sessionRegistry.getSessionInformation(userDetails.getUsername()).getSession();
//another way to do it
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
HttpSession session = ((WebAuthenticationDetails) authentication.getDetails()).getSession();
// Work with the raw session object
}
}
}
In this example, the SessionService
class obtains the HttpSession
object by retrieving the authenticated user’s UserDetails
from the SecurityContext
and using the SessionRegistry
to get the session information for that user.
By leveraging these session management features in Spring Security, you can enhance the security of your application and work with session data more effectively, allowing you to maintain user-specific state and information across multiple requests.
Conclusion:
Spring Security provides a comprehensive set of features and configurations for managing sessions in web applications. By understanding these mechanisms, you can control concurrent sessions, set session timeouts, handle session expiration, and prevent potential security risks associated with session tracking.
Additionally, Spring Security’s integration with servlet containers allows you to leverage the underlying session management capabilities, ensuring a seamless and secure session management experience for your application users.