HTTP session related functonality is handled by a combination of the SessionManagementFilter
and the SessionAuthenticationStrategy
interface, which the filter delegates to. Typical usage includes session-fixation protection attack prevention, detection of session timeouts and restrictions on how many sessions an authenticated user may have open concurrently.
The SessionManagementFilter
checks the contents of the SecurityContextRepository
against the current contents of the SecurityContextHolder
to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me [17]. If the repository contains a security context, the filter does nothing. If it doesn’t, and the thread-local SecurityContext
contains a (non-anonymous) Authentication
object, the filter assumes they have been authenticated by a previous filter in the stack. It will then invoke the configured SessionAuthenticationStrategy
.
If the user is not currently authenticated, the filter will check whether an invalid session ID has been requested (because of a timeout, for example) and will invoke the configured InvalidSessionStrategy
, if one is set. The most common behaviour is just to redirect to a fixed URL and this is encapsulated in the standard implementation SimpleRedirectInvalidSessionStrategy
. The latter is also used when configuring an invalid session URL through the namespace,as described earlier.
SessionAuthenticationStrategy
is used by both SessionManagementFilter
and AbstractAuthenticationProcessingFilter
, so if you are using a customized form-login class, for example, you will need to inject it into both of these. In this case, a typical configuration, combining the namespace and custom beans might look like this:
<http> <custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" /> <session-management session-authentication-strategy-ref="sas"/> </http> <beans:bean id="myAuthFilter" class= "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <beans:property name="sessionAuthenticationStrategy" ref="sas" /> ... </beans:bean> <beans:bean id="sas" class= "org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
Note that the use of the default, SessionFixationProtectionStrategy
may cause issues if you are storing beans in the session which implement HttpSessionBindingListener
, including Spring session-scoped beans. See the Javadoc for this class for more information.
Spring Security is able to prevent a principal from concurrently authenticating to the same application more than a specified number of times. Many ISVs take advantage of this to enforce licensing, whilst network administrators like this feature because it helps prevent people from sharing login names. You can, for example, stop user"Batman" from logging onto the web application from two different sessions. You can either expire their previous login or you can report an error when they try to log in again, preventing the second login. Note that if you are using the second approach, a user who has not explicitly logged out (but who has just closed their browser, for example) will not be able to log in again until their original session expires.
Concurrency control is supported by the namespace, so please check the earlier namespace chapter for the simplest configuration. Sometimes you need to customize things though.
The implementation uses a specialized version of SessionAuthenticationStrategy
, called ConcurrentSessionControlAuthenticationStrategy
.
Note | |
---|---|
Previously the concurrent authentication check was made by the |
To use concurrent session support, you’ll need to add the following to web.xml
:
<listener> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener>
In addition, you will need to add the ConcurrentSessionFilter
to your FilterChainProxy
. The ConcurrentSessionFilter
requires two properties, sessionRegistry
, which generally points to an instance of SessionRegistryImpl
, and expiredUrl
, which points to the page to display when a session has expired. A configuration using the namespace to create the FilterChainProxy
and other default beans might look like this:
<http> <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" /> <custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" /> <session-management session-authentication-strategy-ref="sas"/> </http> <beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter"> <beans:property name="sessionRegistry" ref="sessionRegistry" /> <beans:property name="expiredUrl" value="/session-expired.htm" /> </beans:bean> <beans:bean id="myAuthFilter" class= "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"> <beans:property name="sessionAuthenticationStrategy" ref="sas" /> <beans:property name="authenticationManager" ref="authenticationManager" /> </beans:bean> <beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy"> <beans:constructor-arg> <beans:list> <beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy"> <beans:constructor-arg ref="sessionRegistry"/> <beans:property name="maximumSessions" value="1" /> <beans:property name="exceptionIfMaximumExceeded" value="true" /> </beans:bean> <beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy"> </beans:bean> <beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy"> <beans:constructor-arg ref="sessionRegistry"/> </beans:bean> </beans:list> </beans:constructor-arg> </beans:bean> <beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
Adding the listener to web.xml
causes an ApplicationEvent
to be published to the Spring ApplicationContext
every time a HttpSession
commences or terminates. This is critical, as it allows the SessionRegistryImpl
to be notified when a session ends. Without it, a user will never be able to log back in again once they have exceeded their session allowance, even if they log out of another session or it times out.
Setting up concurrency-control, either through the namespace or using plain beans has the useful side effect of providing you with a reference to the SessionRegistry
which you can use directly within your application, so even if you don’t want to restrict the number of sessions a user may have, it may be worth setting up the infrastructure anyway. You can set the maximumSession
property to -1 to allow unlimited sessions. If you’re using the namespace, you can set an alias for the internally-created SessionRegistry
using the session-registry-alias
attribute, providing a reference which you can inject into your own beans.
The getAllPrincipals()
method supplies you with a list of the currently authenticated users. You can list a user’s sessions by calling the getAllSessions(Object principal, boolean includeExpiredSessions)
method, which returns a list of SessionInformation
objects. You can also expire a user’s session by calling expireNow()
on a SessionInformation
instance. When the user returns to the application, they will be prevented from proceeding. You may find these methods useful in an administration application, for example. Have a look at the Javadoc for more information.
[17] Authentication by mechanisms which perform a redirect after authenticating (such as form-login) will not be detected by SessionManagementFilter
, as the filter will not be invoked during the authenticating request. Session-management functionality has to be handled separately in these cases.