浏览代码

SEC-2057: ConcurrentSessionFilter is now after SecurityContextPersistenceFilter

Previously, ConcurrentSessionFilter was placed after SecurityContextPersistenceFilter
which meant that the SecurityContextHolder was empty when ConcurrentSessionFilter was
invoked. This caused the Authentication to be null when performing a logout. It also
caused complications with LogoutHandler implementations that would be accessing the
SecurityContextHolder and potentially clear it out expecting that
SecurityContextPersistenceFilter would then clear the SecurityContextRepository.

The ConcurrentSessionFilter is now positioned after the
SecurityContextPersistenceFilter to ensure that the SecurityContextHolder is populated
and cleared out appropriately.
Rob Winch 13 年之前
父节点
当前提交
4f741bc914

+ 1 - 1
config/src/main/java/org/springframework/security/config/http/SecurityFilters.java

@@ -10,8 +10,8 @@ package org.springframework.security.config.http;
 enum SecurityFilters {
     FIRST (Integer.MIN_VALUE),
     CHANNEL_FILTER,
-    CONCURRENT_SESSION_FILTER,
     SECURITY_CONTEXT_FILTER,
+    CONCURRENT_SESSION_FILTER,
     LOGOUT_FILTER,
     X509_FILTER,
     PRE_AUTH_FILTER,

+ 19 - 5
config/src/test/groovy/org/springframework/security/config/http/SessionManagementConfigTests.groovy

@@ -106,7 +106,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         }
         createAppContext();
         List filters = getFilters("/someurl");
-        def concurrentSessionFilter = filters.get(0)
+        def concurrentSessionFilter = filters.get(1)
 
         then:
         concurrentSessionFilter instanceof ConcurrentSessionFilter
@@ -134,7 +134,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         createAppContext();
 
         List filters = getFilters("/someurl")
-        ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
+        ConcurrentSessionFilter concurrentSessionFilter = filters.get(1)
         def logoutHandlers = concurrentSessionFilter.handlers
 
         then: 'ConcurrentSessionFilter contains the customized LogoutHandlers'
@@ -159,7 +159,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         createAppContext()
 
         List filters = getFilters("/someurl")
-        ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
+        ConcurrentSessionFilter concurrentSessionFilter = filters.get(1)
         def logoutHandlers = concurrentSessionFilter.handlers
 
         then: 'SecurityContextLogoutHandler and RememberMeServices are in ConcurrentSessionFilter logoutHandlers'
@@ -181,7 +181,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         createAppContext()
 
         List filters = getFilters("/someurl")
-        ConcurrentSessionFilter concurrentSessionFilter = filters.get(0)
+        ConcurrentSessionFilter concurrentSessionFilter = filters.get(1)
         def logoutHandlers = concurrentSessionFilter.handlers
 
         then: 'Only SecurityContextLogoutHandler is found in ConcurrentSessionFilter logoutHandlers'
@@ -191,6 +191,20 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         securityCtxlogoutHandler.invalidateHttpSession == true
     }
 
+    def 'SEC-2057: ConcurrentSessionFilter is after SecurityContextPersistenceFilter'() {
+        httpAutoConfig {
+            'session-management'() {
+                'concurrency-control'()
+            }
+        }
+        createAppContext()
+        List filters = getFilters("/someurl")
+
+        expect:
+        filters.get(0) instanceof SecurityContextPersistenceFilter
+        filters.get(1) instanceof ConcurrentSessionFilter
+    }
+
     def 'concurrency-control handles default expired-url as null'() {
         httpAutoConfig {
             'session-management'() {
@@ -201,7 +215,7 @@ class SessionManagementConfigTests extends AbstractHttpConfigTests {
         List filters = getFilters("/someurl");
 
         expect:
-        filters.get(0).expiredUrl == null
+        filters.get(1).expiredUrl == null
     }
 
     def externalSessionStrategyIsSupported() {

+ 5 - 5
docs/manual/src/docbook/namespace-config.xml

@@ -683,16 +683,16 @@ List&lt;OpenIDAttribute> attributes = token.getAttributes();</programlisting>The
                             <entry><literal>ChannelProcessingFilter</literal></entry>
                             <entry><literal>http/intercept-url@requires-channel</literal></entry>
                         </row>
-                        <row>
-                            <entry> CONCURRENT_SESSION_FILTER</entry>
-                            <entry><literal>ConcurrentSessionFilter</literal> </entry>
-                            <entry><literal>session-management/concurrency-control</literal></entry>
-                        </row>
                         <row>
                             <entry> SECURITY_CONTEXT_FILTER</entry>
                             <entry><classname>SecurityContextPersistenceFilter</classname></entry>
                             <entry><literal>http</literal></entry>
                         </row>
+                        <row>
+                            <entry> CONCURRENT_SESSION_FILTER</entry>
+                            <entry><literal>ConcurrentSessionFilter</literal> </entry>
+                            <entry><literal>session-management/concurrency-control</literal></entry>
+                        </row>
                         <row>
                             <entry> LOGOUT_FILTER </entry>
                             <entry><literal>LogoutFilter</literal></entry>

+ 6 - 6
docs/manual/src/docbook/security-filter-chain.xml

@@ -137,12 +137,6 @@
                 <para><classname>ChannelProcessingFilter</classname>, because it might need to
                     redirect to a different protocol</para>
             </listitem>
-            <listitem>
-                <para><classname>ConcurrentSessionFilter</classname>, because it doesn't use any
-                    <classname>SecurityContextHolder</classname> functionality but needs to update
-                    the <interfacename>SessionRegistry</interfacename> to reflect ongoing requests
-                    from the principal</para>
-            </listitem>
             <listitem>
                 <para><classname>SecurityContextPersistenceFilter</classname>, so a
                     <interfacename>SecurityContext</interfacename> can be set up in the
@@ -151,6 +145,12 @@
                     copied to the <literal>HttpSession</literal> when the web request ends (ready
                     for use with the next web request)</para>
             </listitem>
+            <listitem>
+                <para><classname>ConcurrentSessionFilter</classname>, because it uses the
+                    <classname>SecurityContextHolder</classname> functionality and needs to update
+                    the <interfacename>SessionRegistry</interfacename> to reflect ongoing requests
+                    from the principal</para>
+            </listitem>
             <listitem>
                 <para>Authentication processing mechanisms -
                     <classname>UsernamePasswordAuthenticationFilter</classname>,