浏览代码

SEC-1750: Make sure RunAs replacement is constrained to the SecurityContext of the current thread.

Luke Taylor 14 年之前
父节点
当前提交
76dc21469e

+ 16 - 9
core/src/main/java/org/springframework/security/intercept/AbstractSecurityInterceptor.java

@@ -26,8 +26,10 @@ import org.springframework.security.ConfigAttribute;
 import org.springframework.security.ConfigAttributeDefinition;
 import org.springframework.security.RunAsManager;
 
+import org.springframework.security.context.SecurityContext;
 import org.springframework.security.context.SecurityContextHolder;
 
+import org.springframework.security.context.SecurityContextImpl;
 import org.springframework.security.event.authorization.AuthenticationCredentialsNotFoundEvent;
 import org.springframework.security.event.authorization.AuthorizationFailureEvent;
 import org.springframework.security.event.authorization.AuthorizedEvent;
@@ -110,7 +112,7 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
     protected static final Log logger = LogFactory.getLog(AbstractSecurityInterceptor.class);
 
     //~ Instance fields ================================================================================================
-    
+
     protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
     private ApplicationEventPublisher eventPublisher;
     private AccessDecisionManager accessDecisionManager;
@@ -140,21 +142,22 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
 
         if (token.isContextHolderRefreshRequired()) {
             if (logger.isDebugEnabled()) {
-                logger.debug("Reverting to original Authentication: " + token.getAuthentication().toString());
+                logger.debug("Reverting to original Authentication: " + token.getSecurityContext().getAuthentication());
             }
 
-            SecurityContextHolder.getContext().setAuthentication(token.getAuthentication());
+            SecurityContextHolder.setContext(token.getSecurityContext());
         }
 
         if (afterInvocationManager != null) {
             // Attempt after invocation handling
             try {
-                returnedObject = afterInvocationManager.decide(token.getAuthentication(), token.getSecureObject(),
+                returnedObject = afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
+                        token.getSecureObject(),
                         token.getAttr(), returnedObject);
             }
             catch (AccessDeniedException accessDeniedException) {
                 AuthorizationFailureEvent event = new AuthorizationFailureEvent(token.getSecureObject(), token
-                        .getAttr(), token.getAuthentication(), accessDeniedException);
+                        .getAttr(), token.getSecurityContext().getAuthentication(), accessDeniedException);
                 publishEvent(event);
 
                 throw accessDeniedException;
@@ -285,16 +288,20 @@ public abstract class AbstractSecurityInterceptor implements InitializingBean, A
             }
 
             // no further work post-invocation
-            return new InterceptorStatusToken(authenticated, false, attr, object);
+            return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attr, object);
         } else {
             if (logger.isDebugEnabled()) {
                 logger.debug("Switching to RunAs Authentication: " + runAs);
             }
 
-            SecurityContextHolder.getContext().setAuthentication(runAs);
+            SecurityContext originalContext = SecurityContextHolder.getContext();
+            SecurityContext runAsContext = new SecurityContextImpl();
+            runAsContext.setAuthentication(runAs);
+
+            SecurityContextHolder.setContext(runAsContext);
 
-            // revert to token.Authenticated post-invocation
-            return new InterceptorStatusToken(authenticated, true, attr, object);
+            // revert to original context post-invocation
+            return new InterceptorStatusToken(originalContext, true, attr, object);
         }
     }
 

+ 6 - 5
core/src/main/java/org/springframework/security/intercept/InterceptorStatusToken.java

@@ -17,6 +17,7 @@ package org.springframework.security.intercept;
 
 import org.springframework.security.Authentication;
 import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.context.SecurityContext;
 
 
 /**
@@ -32,16 +33,16 @@ import org.springframework.security.ConfigAttributeDefinition;
 public class InterceptorStatusToken {
     //~ Instance fields ================================================================================================
 
-    private Authentication authentication;
+    private SecurityContext context;
     private ConfigAttributeDefinition attr;
     private Object secureObject;
     private boolean contextHolderRefreshRequired;
 
     //~ Constructors ===================================================================================================
 
-    public InterceptorStatusToken(Authentication authentication, boolean contextHolderRefreshRequired,
+    public InterceptorStatusToken(SecurityContext context, boolean contextHolderRefreshRequired,
         ConfigAttributeDefinition attr, Object secureObject) {
-        this.authentication = authentication;
+        this.context = context;
         this.contextHolderRefreshRequired = contextHolderRefreshRequired;
         this.attr = attr;
         this.secureObject = secureObject;
@@ -53,8 +54,8 @@ public class InterceptorStatusToken {
         return attr;
     }
 
-    public Authentication getAuthentication() {
-        return authentication;
+    public SecurityContext getSecurityContext() {
+        return context;
     }
 
     public Object getSecureObject() {

+ 6 - 3
core/src/test/java/org/springframework/security/intercept/InterceptorStatusTokenTests.java

@@ -18,6 +18,8 @@ package org.springframework.security.intercept;
 import junit.framework.TestCase;
 
 import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextImpl;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
 import org.springframework.security.util.SimpleMethodInvocation;
 
@@ -58,12 +60,13 @@ public class InterceptorStatusTokenTests extends TestCase {
         ConfigAttributeDefinition attr = new ConfigAttributeDefinition("FOO");
         MethodInvocation mi = new SimpleMethodInvocation();
 
-        InterceptorStatusToken token = new InterceptorStatusToken(new UsernamePasswordAuthenticationToken("rod",
-                    "koala"), true, attr, mi);
+        SecurityContext ctx = new SecurityContextImpl();
+
+        InterceptorStatusToken token = new InterceptorStatusToken(ctx, true, attr, mi);
 
         assertTrue(token.isContextHolderRefreshRequired());
         assertEquals(attr, token.getAttr());
         assertEquals(mi, token.getSecureObject());
-        assertEquals("rod", token.getAuthentication().getPrincipal());
+        assertSame(ctx, token.getSecurityContext());
     }
 }

+ 6 - 1
core/src/test/java/org/springframework/security/intercept/method/aopalliance/MethodSecurityInterceptorTests.java

@@ -34,6 +34,7 @@ import org.springframework.security.MockAuthenticationManager;
 import org.springframework.security.MockRunAsManager;
 import org.springframework.security.RunAsManager;
 
+import org.springframework.security.context.SecurityContext;
 import org.springframework.security.context.SecurityContextHolder;
 
 import org.springframework.security.intercept.method.MethodDefinitionSource;
@@ -166,11 +167,15 @@ public class MethodSecurityInterceptorTests extends TestCase {
     public void testMethodCallWithRunAsReplacement() throws Exception {
         UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
                 new GrantedAuthority[] {new GrantedAuthorityImpl("MOCK_UPPER")});
-        SecurityContextHolder.getContext().setAuthentication(token);
+        SecurityContext ctx = SecurityContextHolder.getContext();
+        ctx.setAuthentication(token);
 
         ITargetObject target = makeInterceptedTarget();
         String result = target.makeUpperCase("hello");
         assertEquals("HELLO org.springframework.security.MockRunAsAuthenticationToken true", result);
+        // Check reset afterwards
+        assertSame(ctx, SecurityContextHolder.getContext());
+        assertSame(token, SecurityContextHolder.getContext().getAuthentication());
     }
 
     public void testMethodCallWithoutRunAsReplacement()