Browse Source

SEC-1229: Refactored authentication.concurrent in core, moving classes into core.session

Luke Taylor 16 years ago
parent
commit
acf13c74ca
23 changed files with 119 additions and 150 deletions
  1. 3 3
      config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java
  2. 1 1
      config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java
  3. 0 55
      core/src/main/java/org/springframework/security/authentication/concurrent/ConcurrentSessionController.java
  4. 0 6
      core/src/main/java/org/springframework/security/authentication/concurrent/package.html
  5. 1 1
      core/src/main/java/org/springframework/security/core/session/SessionIdentifierAware.java
  6. 1 1
      core/src/main/java/org/springframework/security/core/session/SessionInformation.java
  7. 1 1
      core/src/main/java/org/springframework/security/core/session/SessionRegistry.java
  8. 2 3
      core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java
  9. 6 0
      core/src/main/java/org/springframework/security/core/session/package.html
  10. 2 2
      core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java
  11. 3 1
      core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java
  12. 9 10
      docs/manual/src/docbook/appendix-namespace.xml
  13. 39 43
      docs/manual/src/docbook/concurrent-sessions.xml
  14. 1 1
      itest/context/src/test/java/org/springframework/security/integration/SEC936ApplicationContextTests.java
  15. 2 2
      itest/context/src/test/resources/sec-936-app-context.xml
  16. 1 1
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
  17. 1 1
      web/src/main/java/org/springframework/security/web/authentication/WebAuthenticationDetails.java
  18. 4 4
      web/src/main/java/org/springframework/security/web/authentication/concurrent/ConcurrentSessionFilter.java
  19. 32 4
      web/src/main/java/org/springframework/security/web/session/ConcurrentSessionControlStrategy.java
  20. 1 1
      web/src/main/java/org/springframework/security/web/session/SessionFixationProtectionStrategy.java
  21. 2 2
      web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
  22. 3 3
      web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java
  23. 4 4
      web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java

+ 3 - 3
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;

+ 1 - 1
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;

+ 0 - 55
core/src/main/java/org/springframework/security/authentication/concurrent/ConcurrentSessionController.java

@@ -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);
-}

+ 0 - 6
core/src/main/java/org/springframework/security/authentication/concurrent/package.html

@@ -1,6 +0,0 @@
-<html>
-<body>
-Concurrent session control and registration classes.
-</body>
-</html>
-

+ 1 - 1
core/src/main/java/org/springframework/security/authentication/concurrent/SessionIdentifierAware.java → 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()}

+ 1 - 1
core/src/main/java/org/springframework/security/authentication/concurrent/SessionInformation.java → 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;
 

+ 1 - 1
core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistry.java → 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;
 

+ 2 - 3
core/src/main/java/org/springframework/security/authentication/concurrent/SessionRegistryImpl.java → 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.
  *

+ 6 - 0
core/src/main/java/org/springframework/security/core/session/package.html

@@ -0,0 +1,6 @@
+<html>
+<body>
+Session registry and other related classes.
+</body>
+</html>
+

+ 2 - 2
core/src/test/java/org/springframework/security/authentication/concurrent/SessionInformationTests.java → 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;
 
 
 /**

+ 3 - 1
core/src/test/java/org/springframework/security/authentication/concurrent/SessionRegistryImplTests.java → 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}.

+ 9 - 10
docs/manual/src/docbook/appendix-namespace.xml

@@ -298,18 +298,17 @@
     <section xml:id="nsa-session-mgmt">
       <title>The <literal>&lt;session-management&gt;</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>

+ 39 - 43
docs/manual/src/docbook/concurrent-sessions.xml

@@ -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>

+ 1 - 1
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;
 

+ 2 - 2
itest/context/src/test/resources/sec-936-app-context.xml

@@ -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>

+ 1 - 1
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;

+ 1 - 1
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;
 

+ 4 - 4
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.
  * <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.

+ 32 - 4
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 <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");

+ 1 - 1
web/src/main/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategy.java → 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());
 
     /**

+ 2 - 2
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");

+ 3 - 3
web/src/test/java/org/springframework/security/web/authentication/ConcurrentSessionFilterTests.java → 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;

+ 4 - 4
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());