diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index 9a387709e2..bcbae721fc 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java @@ -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; diff --git a/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java index b22dac8ea7..9a20812445 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java @@ -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; diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/ConcurrentSessionController.java b/core/src/main/java/org/springframework/security/authentication/concurrent/ConcurrentSessionController.java deleted file mode 100644 index c09e570d51..0000000000 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/ConcurrentSessionController.java +++ /dev/null @@ -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 AuthenticationManagers before they authenticate, but could equally - * include Filters or other interceptors that wish to confirm the ongoing validity of a previously - * authenticated Authentication.

The implementation should throw a suitable exception if the - * user has exceeded their maximum allowed concurrent sessions.

- * - * @param request the authentication request (never null) - * - * @throws AuthenticationException if the user has exceeded their maximum allowed current sessions - */ - void checkAuthenticationAllowed(Authentication request) - throws AuthenticationException; - - /** - * Called by an AuthenticationManager 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 null) - */ - void registerSuccessfulAuthentication(Authentication authentication); -} diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/package.html b/core/src/main/java/org/springframework/security/authentication/concurrent/package.html deleted file mode 100644 index 9235ec5043..0000000000 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/package.html +++ /dev/null @@ -1,6 +0,0 @@ - - -Concurrent session control and registration classes. - - - diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionIdentifierAware.java b/core/src/main/java/org/springframework/security/core/session/SessionIdentifierAware.java similarity index 96% rename from core/src/main/java/org/springframework/security/authentication/concurrent/SessionIdentifierAware.java rename to core/src/main/java/org/springframework/security/core/session/SessionIdentifierAware.java index a84a4468d2..417205c7a8 100644 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionIdentifierAware.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionIdentifierAware.java @@ -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()} diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionInformation.java b/core/src/main/java/org/springframework/security/core/session/SessionInformation.java similarity index 97% rename from core/src/main/java/org/springframework/security/authentication/concurrent/SessionInformation.java rename to core/src/main/java/org/springframework/security/core/session/SessionInformation.java index 0217bf35d4..de1db8bab7 100644 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionInformation.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionInformation.java @@ -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; diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistry.java b/core/src/main/java/org/springframework/security/core/session/SessionRegistry.java similarity index 98% rename from core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistry.java rename to core/src/main/java/org/springframework/security/core/session/SessionRegistry.java index be3bb470a0..9496b923a4 100644 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistry.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionRegistry.java @@ -13,7 +13,7 @@ * limitations under the License. */ -package org.springframework.security.authentication.concurrent; +package org.springframework.security.core.session; import java.util.List; diff --git a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistryImpl.java b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java similarity index 96% rename from core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistryImpl.java rename to core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java index e988793c96..c044ee52f9 100644 --- a/core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistryImpl.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java @@ -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. * diff --git a/core/src/main/java/org/springframework/security/core/session/package.html b/core/src/main/java/org/springframework/security/core/session/package.html new file mode 100644 index 0000000000..690d318501 --- /dev/null +++ b/core/src/main/java/org/springframework/security/core/session/package.html @@ -0,0 +1,6 @@ + + +Session registry and other related classes. + + + diff --git a/core/src/test/java/org/springframework/security/authentication/concurrent/SessionInformationTests.java b/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java similarity index 91% rename from core/src/test/java/org/springframework/security/authentication/concurrent/SessionInformationTests.java rename to core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java index ecaffc5f62..d053423e5d 100644 --- a/core/src/test/java/org/springframework/security/authentication/concurrent/SessionInformationTests.java +++ b/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java @@ -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; /** diff --git a/core/src/test/java/org/springframework/security/authentication/concurrent/SessionRegistryImplTests.java b/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java similarity index 97% rename from core/src/test/java/org/springframework/security/authentication/concurrent/SessionRegistryImplTests.java rename to core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java index fc8e3632bc..6b2fcbecc3 100644 --- a/core/src/test/java/org/springframework/security/authentication/concurrent/SessionRegistryImplTests.java +++ b/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java @@ -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}. diff --git a/docs/manual/src/docbook/appendix-namespace.xml b/docs/manual/src/docbook/appendix-namespace.xml index 26733a136f..2e34f199ff 100644 --- a/docs/manual/src/docbook/appendix-namespace.xml +++ b/docs/manual/src/docbook/appendix-namespace.xml @@ -298,18 +298,17 @@
The <literal><session-management></literal> Element Session-management related functionality is implemented by the addition of a - SessionManagementFilter to the filter stack. + SessionManagementFilter to the filter stack.
<literal>session-fixation-protection</literal> 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". - - If session fixation protection is enabled, the SessionManagementFilter - is inected with a appropriately configured DefaultSessionAuthenticationStrategy. - See the Javadoc for this class for more details. - + If session fixation protection is enabled, the + SessionManagementFilter is inected with a appropriately + configured DefaultSessionAuthenticationStrategy. See the Javadoc + for this class for more details.
@@ -317,15 +316,15 @@ Adds support for concurrent session control, allowing limits to be placed on the number of active sessions a user can have. A ConcurrentSessionFilter will be created, and a ConcurrentSessionControlStrategy will be used with the - SessionManagementFilter. If a form-login - element has been declared, the strategy object will also be injected into the created - authentication filter. An instance of SessionRegistry (a + SessionManagementFilter. If a form-login element + has been declared, the strategy object will also be injected into the created authentication + filter. An instance of SessionRegistry (a SessionRegistryImpl instance unless the user wishes to use a custom bean) will be created for use by the strategy.
The <literal>max-sessions</literal> attribute Maps to the maximumSessions property of - ConcurrentSessionControllerImpl. + ConcurrentSessionControlStrategy.
The <literal>expired-url</literal> attribute diff --git a/docs/manual/src/docbook/concurrent-sessions.xml b/docs/manual/src/docbook/concurrent-sessions.xml index 755da3986a..8c391121e4 100644 --- a/docs/manual/src/docbook/concurrent-sessions.xml +++ b/docs/manual/src/docbook/concurrent-sessions.xml @@ -14,14 +14,13 @@ The SessionManagementFilter checks the contents of the SecurityContextRepository against the current contents of the SecurityContextHolder 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 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. - . - 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 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. . 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 @@ -34,10 +33,11 @@
<interfacename>SessionAuthenticationStrategy</interfacename> - SessionAuthenticationStrategy is used by both SessionManagementFilter - and AbstractAutheticationProcessingFilter, 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 - SessionAuthenticationStrategy is used by both + SessionManagementFilter and + AbstractAutheticationProcessingFilter, 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: @@ -55,9 +55,7 @@ ]]> - - - +
Concurrency Control @@ -70,15 +68,15 @@ for the simplest configuration. Sometimes you need to customize things though. The implementation uses a specialized version of SessionAuthenticationStrategy, called - ConcurrentSessionControlStrategy. - Previously the concurrent authentication check was made by the - ProviderManager, which could be injected with a - ConcurrentSessionController 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 AuthenticationManager and once - they are successfully authenticated, a session is created and the check is made whether they - are allowed to have another session open. + ConcurrentSessionControlStrategy. Previously the + concurrent authentication check was made by the ProviderManager, + which could be injected with a ConcurrentSessionController. 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 + AuthenticationManager and once they are successfully + authenticated, a session is created and the check is made whether they are allowed to have + another session open. To use concurrent session support, you'll need to add the following to web.xml: @@ -87,15 +85,13 @@ ]]> - In addition, you will need to add the - org.springframework.security.web.authentication.concurrent.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: - 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: @@ -104,7 +100,7 @@ + class="org.springframework.security.web.session.ConcurrentSessionFilter"> @@ -121,15 +117,15 @@ - + ]]> - - - 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. + + 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.
diff --git a/itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java b/itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java index 15a3f68c9c..5aa71b3091 100644 --- a/itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java +++ b/itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java @@ -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; diff --git a/itest/context/src/test/resources/sec-936-app-context.xml b/itest/context/src/test/resources/sec-936-app-context.xml index 57ca6e7f71..1e05c252bf 100755 --- a/itest/context/src/test/resources/sec-936-app-context.xml +++ b/itest/context/src/test/resources/sec-936-app-context.xml @@ -31,12 +31,12 @@ - org.springframework.security.authentication.concurrent.SessionRegistry.get*=ROLE_C + org.springframework.security.core.session.SessionRegistry.get*=ROLE_C - + securityInterceptor diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java index caa8d22172..c3279690bf 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java @@ -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; diff --git a/web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java b/web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java index d362fefd65..8169fa07ed 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java +++ b/web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java @@ -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; diff --git a/web/src/main/java/org/springframework/security/web/authentication/concurrent/ConcurrentSessionFilter.java b/web/src/main/java/org/springframework/security/web/authentication/concurrent/ConcurrentSessionFilter.java index 962d0b2716..5472aa9554 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/concurrent/ConcurrentSessionFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/concurrent/ConcurrentSessionFilter.java @@ -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. *

* 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 SessionRegistry + * {@link org.springframework.security.core.session.SessionInformation} from the SessionRegistry * 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. diff --git a/web/src/main/java/org/springframework/security/web/session/ConcurrentSessionControlStrategy.java b/web/src/main/java/org/springframework/security/web/session/ConcurrentSessionControlStrategy.java index 35ef91cefe..c42baa0c3d 100644 --- a/web/src/main/java/org/springframework/security/web/session/ConcurrentSessionControlStrategy.java +++ b/web/src/main/java/org/springframework/security/web/session/ConcurrentSessionControlStrategy.java @@ -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 maximumSessions value. + * The {@link SessionRegistry} is used as the source of data on authenticated users and session data. + *

+ * If a user has reached the maximum number of permitted sessions, the behaviour depends on the + * exceptionIfMaxExceeded property. The default behaviour is to expired the least recently used session, which + * will be invalidated by the {@link ConcurrentSessionFilter} if accessed again. If exceptionIfMaxExceeded is + * set to true, however, the user will be prevented from starting a new authenticated session. + *

+ * 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 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 exceptionIfMaximumExceeded property, which determines whether the user should be prevented + * from opening more sessions than allowed. If set to true, a SessionAuthenticationException + * will be raised. + * + * @param exceptionIfMaximumExceeded defaults to false. + */ public void setExceptionIfMaximumExceeded(boolean exceptionIfMaximumExceeded) { this.exceptionIfMaximumExceeded = exceptionIfMaximumExceeded; } + /** + * Sets the maxSessions 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"); diff --git a/web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java b/web/src/main/java/org/springframework/security/web/session/SessionFixationProtectionStrategy.java similarity index 98% rename from web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java rename to web/src/main/java/org/springframework/security/web/session/SessionFixationProtectionStrategy.java index bd7bd38788..dfd2a4b7bb 100644 --- a/web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java +++ b/web/src/main/java/org/springframework/security/web/session/SessionFixationProtectionStrategy.java @@ -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()); /** diff --git a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java index e8e5f0aeea..ca972c3731 100644 --- a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java +++ b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java @@ -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"); diff --git a/web/src/test/java/org/springframework/security/web/authentication/ConcurrentSessionFilterTests.java b/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java similarity index 96% rename from web/src/test/java/org/springframework/security/web/authentication/ConcurrentSessionFilterTests.java rename to web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java index df9b173de6..e2a261eb27 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/ConcurrentSessionFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java @@ -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; diff --git a/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java b/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java index 5119a384a0..5d796b9f2f 100644 --- a/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java @@ -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());