mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-02-25 17:06:42 +00:00
SEC-1229: Refactored authentication.concurrent in core, moving classes into core.session
This commit is contained in:
parent
2b89ebdfbb
commit
acf13c74ca
@ -24,8 +24,8 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.access.vote.AffirmativeBased;
|
||||
import org.springframework.security.access.vote.AuthenticatedVoter;
|
||||
import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
@ -42,7 +42,7 @@ import org.springframework.security.web.authentication.concurrent.ConcurrentSess
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionControlStrategy;
|
||||
import org.springframework.security.web.session.DefaultSessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.session.SessionFixationProtectionStrategy;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.util.AntUrlPathMatcher;
|
||||
import org.springframework.security.web.util.UrlMatcher;
|
||||
@ -248,7 +248,7 @@ class HttpConfigurationBuilder {
|
||||
}
|
||||
} else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)
|
||||
|| StringUtils.hasText(sessionAuthStratRef)) {
|
||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
|
||||
sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(SessionFixationProtectionStrategy.class);
|
||||
} else {
|
||||
sfpf = null;
|
||||
return;
|
||||
|
@ -27,11 +27,11 @@ import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.PostProcessedMockUserDetailsService;
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.openid.OpenID4JavaConsumer;
|
||||
import org.springframework.security.openid.OpenIDAttribute;
|
||||
import org.springframework.security.openid.OpenIDAuthenticationProcessingFilter;
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
|
||||
/**
|
||||
* Provides two methods that can be called by an {@link
|
||||
* org.springframework.security.authentication.AuthenticationManager} to integrate with the
|
||||
* concurrent session handling infrastructure.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
*/
|
||||
public interface ConcurrentSessionController {
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
/**
|
||||
* Called by any class that wishes to know whether the current authentication request should be permitted.
|
||||
* Generally callers will be <code>AuthenticationManager</code>s before they authenticate, but could equally
|
||||
* include <code>Filter</code>s or other interceptors that wish to confirm the ongoing validity of a previously
|
||||
* authenticated <code>Authentication</code>.<p>The implementation should throw a suitable exception if the
|
||||
* user has exceeded their maximum allowed concurrent sessions.</p>
|
||||
*
|
||||
* @param request the authentication request (never <code>null</code>)
|
||||
*
|
||||
* @throws AuthenticationException if the user has exceeded their maximum allowed current sessions
|
||||
*/
|
||||
void checkAuthenticationAllowed(Authentication request)
|
||||
throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Called by an <code>AuthenticationManager</code> when the authentication was successful. An
|
||||
* implementation is expected to register the authenticated user in some sort of registry, for future concurrent
|
||||
* tracking via the {@link #checkAuthenticationAllowed(Authentication)} method.
|
||||
*
|
||||
* @param authentication the successfully authenticated user (never <code>null</code>)
|
||||
*/
|
||||
void registerSuccessfulAuthentication(Authentication authentication);
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Concurrent session control and registration classes.
|
||||
</body>
|
||||
</html>
|
||||
|
@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
/**
|
||||
* Implemented by {@link org.springframework.security.core.Authentication#getDetails()}
|
@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -28,11 +28,10 @@ import java.util.Set;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link org.springframework.security.authentication.concurrent.SessionRegistry}
|
||||
* Base implementation of {@link org.springframework.security.core.session.SessionRegistry}
|
||||
* which also listens for {@link org.springframework.security.web.session.HttpSessionDestroyedEvent}s
|
||||
* published in the Spring application context.
|
||||
*
|
@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Session registry and other related classes.
|
||||
</body>
|
||||
</html>
|
||||
|
@ -13,13 +13,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
|
||||
|
||||
/**
|
@ -13,7 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.authentication.concurrent;
|
||||
package org.springframework.security.core.session;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@ -24,6 +24,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.session.SessionDestroyedEvent;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
|
||||
/**
|
||||
* Tests {@link SessionRegistryImpl}.
|
@ -298,18 +298,17 @@
|
||||
<section xml:id="nsa-session-mgmt">
|
||||
<title>The <literal><session-management></literal> Element</title>
|
||||
<para>Session-management related functionality is implemented by the addition of a
|
||||
<classname>SessionManagementFilter</classname> to the filter stack.</para>
|
||||
<classname>SessionManagementFilter</classname> to the filter stack.</para>
|
||||
<section xml:id="session-fixation-protection">
|
||||
<title><literal>session-fixation-protection</literal></title>
|
||||
<para> Indicates whether an existing session should be invalidated when a user authenticates
|
||||
and a new session started. If set to "none" no change will be made. "newSession" will
|
||||
create a new empty session. "migrateSession" will create a new session and copy the
|
||||
session attributes to the new session. Defaults to "migrateSession".</para>
|
||||
<para>
|
||||
If session fixation protection is enabled, the <classname>SessionManagementFilter</classname>
|
||||
is inected with a appropriately configured <classname>DefaultSessionAuthenticationStrategy</classname>.
|
||||
See the Javadoc for this class for more details.
|
||||
</para>
|
||||
<para> If session fixation protection is enabled, the
|
||||
<classname>SessionManagementFilter</classname> is inected with a appropriately
|
||||
configured <classname>DefaultSessionAuthenticationStrategy</classname>. See the Javadoc
|
||||
for this class for more details. </para>
|
||||
</section>
|
||||
</section>
|
||||
<section xml:id="nsa-concurrent-session-control">
|
||||
@ -317,15 +316,15 @@
|
||||
<para> Adds support for concurrent session control, allowing limits to be placed on the number
|
||||
of active sessions a user can have. A <classname>ConcurrentSessionFilter</classname> will be
|
||||
created, and a <classname>ConcurrentSessionControlStrategy</classname> will be used with the
|
||||
<classname>SessionManagementFilter</classname>. If a <literal>form-login</literal>
|
||||
element has been declared, the strategy object will also be injected into the created
|
||||
authentication filter. An instance of <interfacename>SessionRegistry</interfacename> (a
|
||||
<classname>SessionManagementFilter</classname>. If a <literal>form-login</literal> element
|
||||
has been declared, the strategy object will also be injected into the created authentication
|
||||
filter. An instance of <interfacename>SessionRegistry</interfacename> (a
|
||||
<classname>SessionRegistryImpl</classname> instance unless the user wishes to use a custom
|
||||
bean) will be created for use by the strategy.</para>
|
||||
<section>
|
||||
<title>The <literal>max-sessions</literal> attribute</title>
|
||||
<para>Maps to the <literal>maximumSessions</literal> property of
|
||||
<classname>ConcurrentSessionControllerImpl</classname>.</para>
|
||||
<classname>ConcurrentSessionControlStrategy</classname>.</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>The <literal>expired-url</literal> attribute</title>
|
||||
|
@ -14,14 +14,13 @@
|
||||
<para>The <classname>SessionManagementFilter</classname> checks the contents of the
|
||||
<interfacename>SecurityContextRepository</interfacename> against the current contents of the
|
||||
<classname>SecurityContextHolder</classname> to deterine whether a user has been
|
||||
authenticated during the current request, typically by a non-interactive authentication mechanism, such
|
||||
as pre-authentication or remember-me <footnote><para>Authentication by mechanisms which perform a redirect
|
||||
after authenticating (such as form-login) will not be detected by <classname>SessionManagementFilter</classname>,
|
||||
as the filter will not be invoked during the authenticating request. Session-management functionality has to be
|
||||
handled separately in these cases.
|
||||
</para></footnote>.
|
||||
If the repository contains a security context, the
|
||||
filter does nothing. If it doesn't, and the thread-local
|
||||
authenticated during the current request, typically by a non-interactive authentication
|
||||
mechanism, such as pre-authentication or remember-me <footnote><para>Authentication by
|
||||
mechanisms which perform a redirect after authenticating (such as form-login) will not be
|
||||
detected by <classname>SessionManagementFilter</classname>, as the filter will not be
|
||||
invoked during the authenticating request. Session-management functionality has to be
|
||||
handled separately in these cases. </para></footnote>. If the repository contains a
|
||||
security context, the filter does nothing. If it doesn't, and the thread-local
|
||||
<interfacename>SecurityContext</interfacename> contains a (non-anonymous)
|
||||
<interfacename>Authentication</interfacename> object, the filter assumes they have been
|
||||
authenticated by a previous filter in the stack. It will then invoke the configured
|
||||
@ -34,10 +33,11 @@
|
||||
<section>
|
||||
<title><interfacename>SessionAuthenticationStrategy</interfacename></title>
|
||||
<para>
|
||||
<interfacename>SessionAuthenticationStrategy</interfacename> is used by both <classname>SessionManagementFilter</classname>
|
||||
and <classname>AbstractAutheticationProcessingFilter</classname>, 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
|
||||
<programlisting><![CDATA[
|
||||
<interfacename>SessionAuthenticationStrategy</interfacename> is used by both
|
||||
<classname>SessionManagementFilter</classname> and
|
||||
<classname>AbstractAutheticationProcessingFilter</classname>, 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:<programlisting><![CDATA[
|
||||
<http>
|
||||
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||
<session-management session-authentication-strategy-ref="sas"/>
|
||||
@ -55,9 +55,7 @@
|
||||
<beans:property name="maximumSessions" value="1" />
|
||||
</beans:bean>
|
||||
]]>
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
</programlisting></para>
|
||||
</section>
|
||||
<section xml:id="concurrent-sessions">
|
||||
<title>Concurrency Control</title>
|
||||
@ -70,15 +68,15 @@
|
||||
for the simplest configuration. Sometimes you need to customize things though. </para>
|
||||
<para>The implementation uses a specialized version of
|
||||
<interfacename>SessionAuthenticationStrategy</interfacename>, called
|
||||
<classname>ConcurrentSessionControlStrategy</classname>.
|
||||
<note><para>Previously the concurrent authentication check was made by the
|
||||
<classname>ProviderManager</classname>, which could be injected with a
|
||||
<literal>ConcurrentSessionController</literal> which would check if the user was
|
||||
attempting to exceed the number of sessions permitted. However, this approach required that
|
||||
an HTTP session be created in advance, which is undesirable. In Spring Security 3, the user
|
||||
is first authenticated by the <interfacename>AuthenticationManager</interfacename> and once
|
||||
they are successfully authenticated, a session is created and the check is made whether they
|
||||
are allowed to have another session open.</para></note></para>
|
||||
<classname>ConcurrentSessionControlStrategy</classname>. <note><para>Previously the
|
||||
concurrent authentication check was made by the <classname>ProviderManager</classname>,
|
||||
which could be injected with a <literal>ConcurrentSessionController</literal>. The latter
|
||||
would check if the user was attempting to exceed the number of permitted sessions.
|
||||
However, this approach required that an HTTP session be created in advance, which is
|
||||
undesirable. In Spring Security 3, the user is first authenticated by the
|
||||
<interfacename>AuthenticationManager</interfacename> and once they are successfully
|
||||
authenticated, a session is created and the check is made whether they are allowed to have
|
||||
another session open.</para></note></para>
|
||||
<para>To use concurrent session support, you'll need to add the following to
|
||||
<literal>web.xml</literal>: <programlisting><![CDATA[
|
||||
<listener>
|
||||
@ -87,15 +85,13 @@
|
||||
</listener-class>
|
||||
</listener> ]]>
|
||||
</programlisting></para>
|
||||
<para>In addition, you will need to add the
|
||||
<literal>org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter</literal>
|
||||
to your <classname>FilterChainProxy</classname>. The
|
||||
<classname>ConcurrentSessionFilter</classname> requires two properties,
|
||||
<literal>sessionRegistry</literal>, which generally points to an instance of
|
||||
<literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which points to
|
||||
the page to display when a session has expired. A configuration using the namespace to create the
|
||||
<classname>FilterChainProxy</classname> and other default beans might look like this:
|
||||
<programlisting><![CDATA[
|
||||
<para>In addition, you will need to add the <literal>ConcurrentSessionFilter</literal> to your
|
||||
<classname>FilterChainProxy</classname>. The <classname>ConcurrentSessionFilter</classname>
|
||||
requires two properties, <literal>sessionRegistry</literal>, which generally points to an
|
||||
instance of <literal>SessionRegistryImpl</literal>, and <literal>expiredUrl</literal>, which
|
||||
points to the page to display when a session has expired. A configuration using the namespace
|
||||
to create the <classname>FilterChainProxy</classname> and other default beans might look like
|
||||
this: <programlisting><![CDATA[
|
||||
<http>
|
||||
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
|
||||
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" ref="myAuthFilter" />
|
||||
@ -104,7 +100,7 @@
|
||||
</http>
|
||||
|
||||
<beans:bean id="concurrencyFilter"
|
||||
class="org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter">
|
||||
class="org.springframework.security.web.session.ConcurrentSessionFilter">
|
||||
<beans:property name="sessionRegistry" ref="sessionRegistry" />
|
||||
<beans:property name="expiredUrl" value="/session-expired.htm" />
|
||||
</beans:bean>
|
||||
@ -121,15 +117,15 @@
|
||||
<beans:property name="maximumSessions" value="1" />
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="sessionRegistry" class="org.springframework.security.authentication.concurrent.SessionRegistryImpl" />
|
||||
<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>Adding the listener to <filename>web.xml</filename> causes an <literal>ApplicationEvent</literal> to
|
||||
be published to the Spring <literal>ApplicationContext</literal> every time a
|
||||
<literal>HttpSession</literal> commences or terminates. This is critical, as it allows the
|
||||
<classname>SessionRegistryImpl</classname> 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.</para>
|
||||
</programlisting></para>
|
||||
<para>Adding the listener to <filename>web.xml</filename> causes an
|
||||
<literal>ApplicationEvent</literal> to be published to the Spring
|
||||
<literal>ApplicationContext</literal> every time a <literal>HttpSession</literal> commences
|
||||
or terminates. This is critical, as it allows the <classname>SessionRegistryImpl</classname>
|
||||
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.</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
@ -5,8 +5,8 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
|
@ -31,12 +31,12 @@
|
||||
<property name="authenticationManager" ref="authenticationManager"/>
|
||||
<property name="accessDecisionManager" ref="accessDecisionManager"/>
|
||||
<property name="securityMetadataSource"><value>
|
||||
org.springframework.security.authentication.concurrent.SessionRegistry.get*=ROLE_C
|
||||
org.springframework.security.core.session.SessionRegistry.get*=ROLE_C
|
||||
</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="httpRemoteService" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="proxyInterfaces" value="org.springframework.security.authentication.concurrent.SessionRegistry"/>
|
||||
<property name="proxyInterfaces" value="org.springframework.security.core.session.SessionRegistry"/>
|
||||
<property name="interceptorNames">
|
||||
<list>
|
||||
<value>securityInterceptor</value>
|
||||
|
@ -37,8 +37,8 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.session.NullAuthenticatedSessionStrategy;
|
||||
import org.springframework.security.web.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
package org.springframework.security.web.authentication;
|
||||
|
||||
import org.springframework.security.authentication.concurrent.SessionIdentifierAware;
|
||||
import org.springframework.security.core.session.SessionIdentifierAware;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
@ -25,10 +25,10 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
@ -42,9 +42,9 @@ import org.springframework.web.filter.GenericFilterBean;
|
||||
* Filter required by concurrent session handling package.
|
||||
* <p>
|
||||
* This filter performs two functions. First, it calls
|
||||
* {@link org.springframework.security.authentication.concurrent.SessionRegistry#refreshLastRequest(String)} for each request
|
||||
* {@link org.springframework.security.core.session.SessionRegistry#refreshLastRequest(String)} for each request
|
||||
* so that registered sessions always have a correct "last update" date/time. Second, it retrieves a
|
||||
* {@link org.springframework.security.authentication.concurrent.SessionInformation} from the <code>SessionRegistry</code>
|
||||
* {@link org.springframework.security.core.session.SessionInformation} from the <code>SessionRegistry</code>
|
||||
* for each request and checks if the session has been marked as expired.
|
||||
* If it has been marked as expired, the configured logout handlers will be called (as happens with
|
||||
* {@link org.springframework.security.web.authentication.logout.LogoutFilter}), typically to invalidate the session.
|
||||
|
@ -9,20 +9,36 @@ import javax.servlet.http.HttpSession;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.concurrent.SessionInformation;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.session.SessionInformation;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Strategy which handles concurrent session-control, in addition to the functionality provided by the base class.
|
||||
*
|
||||
* When invoked following an authentication, it will check whether the user in question should be allowed to proceed,
|
||||
* by comparing the number of sessions they already have active with the configured <tt>maximumSessions</tt> value.
|
||||
* The {@link SessionRegistry} is used as the source of data on authenticated users and session data.
|
||||
* <p>
|
||||
* If a user has reached the maximum number of permitted sessions, the behaviour depends on the
|
||||
* <tt>exceptionIfMaxExceeded</tt> property. The default behaviour is to expired the least recently used session, which
|
||||
* will be invalidated by the {@link ConcurrentSessionFilter} if accessed again. If <tt>exceptionIfMaxExceeded</tt> is
|
||||
* set to <tt>true</tt>, however, the user will be prevented from starting a new authenticated session.
|
||||
* <p>
|
||||
* This strategy can be injected into both the {@link SessionManagementFilter} and instances of
|
||||
* {@link AbstractAuthenticationProcessingFilter} (typically {@link UsernamePasswordAuthenticationProcessingFilter}).
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticationStrategy
|
||||
public class ConcurrentSessionControlStrategy extends SessionFixationProtectionStrategy
|
||||
implements MessageSourceAware {
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
private final SessionRegistry sessionRegistry;
|
||||
@ -106,7 +122,7 @@ public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticati
|
||||
*
|
||||
*/
|
||||
protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
|
||||
SessionRegistry registry) {
|
||||
SessionRegistry registry) throws SessionAuthenticationException {
|
||||
if (exceptionIfMaximumExceeded || (sessions == null)) {
|
||||
throw new SessionAuthenticationException(messages.getMessage("ConcurrentSessionControllerImpl.exceededAllowed",
|
||||
new Object[] {new Integer(allowableSessions)},
|
||||
@ -133,10 +149,22 @@ public class ConcurrentSessionControlStrategy extends DefaultSessionAuthenticati
|
||||
sessionRegistry.registerNewSession(newSession.getId(), auth.getPrincipal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <tt>exceptionIfMaximumExceeded</tt> property, which determines whether the user should be prevented
|
||||
* from opening more sessions than allowed. If set to <tt>true</tt>, a <tt>SessionAuthenticationException</tt>
|
||||
* will be raised.
|
||||
*
|
||||
* @param exceptionIfMaximumExceeded defaults to <tt>false</tt>.
|
||||
*/
|
||||
public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) {
|
||||
this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <tt>maxSessions</tt> property. The default value is 1. Use -1 for unlimited sessions.
|
||||
*
|
||||
* @param maximumSessions the maximimum number of permitted sessions a user can have open simultaneously.
|
||||
*/
|
||||
public void setMaximumSessions(int maximumSessions) {
|
||||
Assert.isTrue(maximumSessions != 0,
|
||||
"MaximumLogins must be either -1 to allow unlimited logins, or a positive integer to specify a maximum");
|
||||
|
@ -32,7 +32,7 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
* @version $Id$
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DefaultSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
|
||||
public class SessionFixationProtectionStrategy implements SessionAuthenticationStrategy {
|
||||
protected final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
/**
|
@ -39,7 +39,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final SecurityContextRepository securityContextRepository;
|
||||
private SessionAuthenticationStrategy sessionStrategy = new DefaultSessionAuthenticationStrategy();
|
||||
private SessionAuthenticationStrategy sessionStrategy = new SessionFixationProtectionStrategy();
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
private String invalidSessionUrl;
|
||||
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||
@ -96,7 +96,7 @@ public class SessionManagementFilter extends GenericFilterBean {
|
||||
* Sets the strategy object which handles the session management behaviour when a
|
||||
* user has been authenticated during the current request.
|
||||
*
|
||||
* @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used.
|
||||
* @param sessionStrategy the strategy object. If not set, a {@link SessionFixationProtectionStrategy} is used.
|
||||
*/
|
||||
public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
|
||||
Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
|
||||
|
@ -13,15 +13,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.authentication;
|
||||
package org.springframework.security.web.concurrent;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.springframework.mock.web.MockFilterConfig;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistry;
|
||||
import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
|
||||
import org.springframework.security.core.session.SessionRegistry;
|
||||
import org.springframework.security.core.session.SessionRegistryImpl;
|
||||
import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
|
||||
|
||||
import javax.servlet.Filter;
|
@ -21,7 +21,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
||||
|
||||
@Test
|
||||
public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception {
|
||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||
HttpServletRequest request = new MockHttpServletRequest();
|
||||
|
||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
||||
@ -31,7 +31,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
||||
|
||||
@Test
|
||||
public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception {
|
||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||
HttpServletRequest request = new MockHttpServletRequest();
|
||||
String sessionId = request.getSession().getId();
|
||||
|
||||
@ -43,7 +43,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
||||
// See SEC-1077
|
||||
@Test
|
||||
public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception {
|
||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||
strategy.setMigrateSessionAttributes(false);
|
||||
HttpServletRequest request = new MockHttpServletRequest();
|
||||
HttpSession session = request.getSession();
|
||||
@ -58,7 +58,7 @@ public class DefaultSessionAuthenticationStrategyTests {
|
||||
|
||||
@Test
|
||||
public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception {
|
||||
DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy();
|
||||
SessionFixationProtectionStrategy strategy = new SessionFixationProtectionStrategy();
|
||||
strategy.setAlwaysCreateSession(true);
|
||||
HttpServletRequest request = new MockHttpServletRequest();
|
||||
strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse());
|
||||
|
Loading…
x
Reference in New Issue
Block a user