Jelajahi Sumber

SEC-1396: Implement eager saving of SecurityContext in SessionManagementFilter on authentication.

The user is then seen as being authenticated to further (re-entrant) requests which occur before the existing request has completed. The saving logic is contained with the SecurityContextRepository implementation.
Luke Taylor 15 tahun lalu
induk
melakukan
dcbdfc2026

+ 10 - 4
config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java

@@ -36,6 +36,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
 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.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.session.SessionRegistryImpl;
 import org.springframework.security.openid.OpenID4JavaConsumer;
@@ -77,6 +78,7 @@ import org.springframework.security.web.authentication.rememberme.TokenBasedReme
 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
 import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
 import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
+import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper;
 import org.springframework.security.web.context.SecurityContextPersistenceFilter;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
 import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
@@ -790,13 +792,17 @@ public class HttpSecurityBeanDefinitionParserTests {
         // Register 2 sessions and then check a third
 //        req.setSession(new MockHttpSession());
 //        auth.setDetails(new WebAuthenticationDetails(req));
-        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletResponse mockResponse = new MockHttpServletResponse();
+        SaveContextOnUpdateOrErrorResponseWrapper response = new SaveContextOnUpdateOrErrorResponseWrapper(mockResponse, false) {
+            protected void saveContext(SecurityContext context) {
+            }
+        };
         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
-        assertNull(response.getRedirectedUrl());
+        assertNull(mockResponse.getRedirectedUrl());
         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
-        assertNull(response.getRedirectedUrl());
+        assertNull(mockResponse.getRedirectedUrl());
         seshFilter.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
-        assertEquals("/max-exceeded", response.getRedirectedUrl());
+        assertEquals("/max-exceeded", mockResponse.getRedirectedUrl());
     }
 
     @Test

+ 3 - 3
web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java

@@ -98,7 +98,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
     }
 
     public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
-        SaveToSessionResponseWrapper responseWrapper = (SaveToSessionResponseWrapper)response;
+        SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response;
         // saveContext() might already be called by the response wrapper
         // if something in the chain called sendError() or sendRedirect(). This ensures we only call it
         // once per request.
@@ -289,7 +289,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
      * Stores the necessary state from the start of the request in order to make a decision about whether
      * the security context has changed before saving it.
      */
-    class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {
+    final class SaveToSessionResponseWrapper extends SaveContextOnUpdateOrErrorResponseWrapper {
 
         private HttpServletRequest request;
         private boolean httpSessionExistedAtStartOfRequest;
@@ -327,7 +327,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
          *
          */
         @Override
-        void saveContext(SecurityContext context) {
+        protected void saveContext(SecurityContext context) {
             // See SEC-776
             if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {
                 if (logger.isDebugEnabled()) {

+ 1 - 1
web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java

@@ -42,7 +42,7 @@ public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends HttpServ
      *
      * @param context the <tt>SecurityContext</tt> instance to store
      */
-    abstract void saveContext(SecurityContext context);
+    protected abstract void saveContext(SecurityContext context);
 
     /**
      * Makes sure the session is updated before calling the

+ 3 - 0
web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java

@@ -78,6 +78,9 @@ public class SessionManagementFilter extends GenericFilterBean {
 
                     return;
                 }
+                // Eagerly save the security context to make it available for any possible re-entrant
+                // requests which may occur before the current request completes. SEC-1396.
+                securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
             } else {
              // No security context or authentication present. Check for a session timeout
                 if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {