1. Overview
In this tutorial, we’re going to illustrate how Spring Security allows us to control our HTTP Sessions.
This control ranges from a session timeout to enabling concurrent sessions and other advanced security configs.
2. When Is the Session Created?
We can control exactly when our session gets created and how Spring Security will interact with it:
- always – A session will always be created if one doesn’t already exist.
- ifRequired – A session will be created only if required (default).
- never – The framework will never create a session itself, but it will use one if it already exists.
- stateless – No session will be created or used by Spring Security.
<http create-session="ifRequired">...</http>
Here is the Java configuration:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.sessionManagement(httpSecuritySessionManagementConfigurer ->
httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
return http.build();
}
It’s very important to understand that this configuration only controls what Spring Security does, not the entire application. Spring Security won’t create the session if we instruct it not to, but our app might!
By default, Spring Security will create a session when it needs one — this is “ifRequired“.
For a more stateless application, the “never” option will ensure that Spring Security itself won’t create any session. But if the application creates one, Spring Security will make use of it.
Finally, the strictest session creation option, “stateless“, is a guarantee that the application won’t create any session at all.
This was introduced in Spring 3.1 and will effectively skip parts of the Spring Security filter chain — mainly the session-related parts such as HttpSessionSecurityContextRepository, SessionManagementFilter and RequestCacheFilter.
These more strict control mechanisms have the direct implication that cookies are not used, and so each and every request needs to be re-authenticated.
This stateless architecture plays well with REST APIs and their Statelessness constraint. They also work well with authentication mechanisms such as Basic and Digest Authentication.
3. Under the Hood
Before running the Authentication process, Spring Security will run a filter responsible for storing the Security Context between requests. This is the SecurityContextPersistenceFilter.
The context will be stored according to the strategy HttpSessionSecurityContextRepository by default, which uses the HTTP Session as storage.
For the strict create-session=”stateless” attribute, this strategy will be replaced with another — NullSecurityContextRepository — and no session will be created or used to keep the context.
4. Concurrent Session Control
When a user that is already authenticated tries to authenticate again, the application can deal with that event in one of a few ways. It can either invalidate the active session of the user and authenticate the user again with a new session, or allow both sessions to exist concurrently.
The first step in enabling the concurrent session-control support is to add the following listener in the web.xml:
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
Or we can define it as a Bean:
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
This is essential to make sure that the Spring Security session registry is notified when the session is destroyed.
In order to allow multiple concurrent sessions for the same user, the
<http ...>
<session-management>
<concurrency-control max-sessions="2" />
</session-management>
</http>
Or we can do this via Java configuration:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(2)
}
5. Session Timeout
5.1. Handling the Session Timeout
After the session has timed out, if the user sends a request with an expired session id, they will be redirected to a URL configurable via the namespace:
<session-management>
<concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>
Similarly, if the user sends a request with a session id that is not expired but entirely invalid, they will also be redirected to a configurable URL:
<session-management invalid-session-url="/invalidSession.html">
...
</session-management>
And here’s the corresponding Java configuration:
http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer
.expiredUrl("/sessionExpired")
.invalidSessionUrl("/invalidSession"));
5.2. Configure the Session Timeout With Spring Boot
We can easily configure the Session timeout value of the embedded server using properties:
server.servlet.session.timeout=15m
If we don’t specify the duration unit, Spring will assume it’s seconds.
In a nutshell, with this configuration, the session will expire after 15 minutes of inactivity. The session is considered invalid after this period of time.
If we configured our project to use Tomcat, we have to keep in mind that it only supports minute precision for session timeout, with a minimum of one minute. This means that if we specify a timeout value of 170s, for example, it will result in a two-minute timeout.
Finally, it’s important to mention that even though Spring Session supports a similar property for this purpose (spring.session.timeout), if that’s not specified, the autoconfiguration will fallback to the value of the property we first mentioned.
6. Prevent Using URL Parameters for Session Tracking
Exposing session information in the URL is a growing security risk (from seventh place in 2007 to second place in 2013 on the OWASP Top 10 List).
Starting with Spring 3.0, the URL rewriting logic that would append the jsessionid to the URL can now be disabled by setting the disable-url-rewriting=”true” in the
Alternatively, starting with Servlet 3.0, the session tracking mechanism can also be configured in the web.xml:
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
and programmatically:
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
This chooses where to store the JSESSIONID — in the cookie or in a URL parameter.
7. Session Fixation Protection With Spring Security
The framework offers protection against typical Session Fixation attacks by configuring what happens to an existing session when the user tries to authenticate again:
<session-management session-fixation-protection="migrateSession"> ...
And here’s the corresponding Java configuration:
http.sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionFixation().migrateSession());
By default, Spring Security has this protection enabled (“migrateSession“). On authentication, a new HTTP Session is created, the old one is invalidated and the attributes from the old session are copied over.
If this is not what we want, two other options are available:
- When “none” is set, the original session will not be invalidated.
- When “newSession” is set, a clean session will be created without any of the attributes from the old session being copied over.
8. Secure Session Cookie
Next, we’ll discuss how to secure our session cookie.
We can use the httpOnly and secure flags to secure our session cookie:
- httpOnly: if true then browser script won’t be able to access the cookie
- secure: if true then the cookie will be sent only over HTTPS connection
We can set those flags for our session cookie in the web.xml:
<session-config>
<session-timeout>1</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
This configuration option is available since Java servlet 3. By default, http-only is true and secure is false.
Let’s also have a look at the corresponding Java configuration:
public class MainWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) throws ServletException {
// ...
sc.getSessionCookieConfig().setHttpOnly(true);
sc.getSessionCookieConfig().setSecure(true);
}
}
If we’re using Spring Boot, we can set these flags in our application.properties:
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
Finally, we can also achieve this manually by using a Filter:
public class SessionFilter implements Filter {
@Override
public void doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
Cookie[] allCookies = req.getCookies();
if (allCookies != null) {
Cookie session =
Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID"))
.findFirst().orElse(null);
if (session != null) {
session.setHttpOnly(true);
session.setSecure(true);
res.addCookie(session);
}
}
chain.doFilter(req, res);
}
}
9. Working With the Session
9.1. Session Scoped Beans
A bean can be defined with session scope simply by using the @Scope annotation on beans declared in the web context:
@Component
@Scope("session")
public class Foo { .. }
or with XML:
<bean id="foo" scope="session"/>
Then the bean can be injected into another bean:
@Autowired
private Foo theFoo;
And Spring will bind the new bean to the lifecycle of the HTTP Session.
9.2. Injecting the Raw Session Into a Controller
The raw HTTP Session can also be injected directly into a Controller method:
@RequestMapping(..)
public void fooMethod(HttpSession session) {
session.setAttribute(Constants.FOO, new Foo());
//...
Foo foo = (Foo) session.getAttribute(Constants.FOO);
}
9.3. Obtaining the Raw Session
The current HTTP Session can also be obtained programmatically via the raw Servlet API:
ServletRequestAttributes attr = (ServletRequestAttributes)
RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create
10. Conclusion
In this article, we discussed managing Sessions with Spring Security.
Also, the Spring Reference contains a very good FAQ on Session Management.
As always, the code presented in this article is available over on GitHub. This is a Maven-based project, so it should be easy to import and run as it is.