Question

A security context on my Spring server uses Spring Security's built-in form login implementation. Currently, login sessions are being stored locally, in memory, by the servlet container. I'd like to replace the way HttpSessions are stored and retrieved with one of my Spring Data Mongo repositories. I looked for one of those "slots" in the Java configuration for session management, but didn't find anything. To be clear, I'm looking for the equivalent of a UserDetailsService, but for sessions.

Here's the relevant snippet of Java configuration in my security configuration class:

...
.sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .and()
.formlogin()
    .loginProcessingUrl("/authentication/login")
    .successHandler(successHandler)
    .usernameParameter("username")
    .passwordParameter("password")
    .failureUrl("/login?error")
    .loginPage("/login")
    .and()
.logout()
    .logoutUrl("/authentication/logout")
    .deleteCookies("JSESSIONID")
    .invalidateHttpSession(true)
    .logoutSuccessUrl("/login")
    .and()
...

As far as I can tell, I'm not doing anything particularly strange. It may not be relevant, but I wanted to show precisely what I'm looking at when I say I couldn't find the correct configuration slot.

I looked closely at the source code for the SecurityContextPersistenceFilter, which seems to be the filter in Spring Security's filter chain responsible for retrieving and storing HttpSessions. It delegates session retrieval to a call to getSession() in HttpServletRequest, and session storage to saveContext() in an injected SecurityContextRepository.

In order to correctly replace the default, server-local session storage mechanism, I see three approaches.

  1. Plug in a Spring Data-backed security context repository into the persistence filter. Then, wrap incoming requests to implement custom getSession() behavior that queries the custom security context repository rather than local storage. The built-in persistence filter will then "do the right thing".

  2. Set up an ObjectPostProcessor to replace the default filter with a custom SecurityContextPersistenceFilter that uses my Spring Data repository directly rather than calling getSession() or a using security context repository. I've actually never used an object post processor before, so if that's not what they're meant for, please tell me.

  3. The last option is not one I'm considering, but it's worth mentioning. I think that underneath all the magic, Spring Security really delegates to the servlet container's implementation of session storage. So one way to change the backing store to Mongo would be to use something like Tomcat's Manager interface to customize the session persistence behavior. This is not something I want to do because it becomes quite separate from Spring, I lose the ability to use my services via dependency injection, and it depends completely on the container, making it difficult to change at whim.

I'm sure that gutting out the session storage and replacing it with a database is a fairly common requirement for Spring servers. How is it usually done? If I'm just missing a configuration option, I'd love to see where it's located. Otherwise, suggestions about which route to take (and why) are what I'm looking for.

Was it helpful?

Solution

In Spring 3 the place is in SessionManagement.

Basically you define the session filter and specialise either the session strategy or the session registry.

The session registry is in charge of dealing with session invalidation and creation. In that point you could persist whatever is that you need to persist.

The downside of this approach is that it requires either that you declare the session event publisher in web.xml file or that you handle everything.

An example would be to implement SessionRegistry and SessionAuthenticationStrategy. From there when a user authenticates or a getSession(true) (or invalidate it) is executed it will reach the code and there you can act upon it. Your strategy would have your session registry injected. If a user authenticates through the authentication chain it would reach your strategy, which would pass the session to your registry.

An alternative approach is to add a custom filter of your own. A class extending GenericFilterBean. And then register it:

<security:custom-filter ref="customSessionFilter" after="LAST" />

In this example it would be executed last. This is useful since you could check for an active session or a successfully authenticated user.

OTHER TIPS

An approach similar to your option #3 without relying on container-specific interfaces would be to use Spring Session with a MongoDB-backed SessionRepository implementation.

This would handle persisting all HTTP Session data rather than only the bits specific to Spring-security.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top