Переглянути джерело

SEC-1229: Added support for parsing error URL in session-management

Luke Taylor 16 роки тому
батько
коміт
ebada9fd12

+ 0 - 72
config/src/main/java/org/springframework/security/config/http/ConcurrentSessionsBeanDefinitionParser.java

@@ -1,72 +0,0 @@
-package org.springframework.security.config.http;
-
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.parsing.BeanComponentDefinition;
-import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.beans.factory.xml.BeanDefinitionParser;
-import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.security.authentication.ProviderManager;
-import org.springframework.security.authentication.concurrent.ConcurrentSessionControllerImpl;
-import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
-import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
-import org.springframework.util.StringUtils;
-import org.w3c.dom.Element;
-
-/**
- * Sets up support for concurrent session support control, creating {@link ConcurrentSessionFilter},
- * {@link SessionRegistryImpl} and {@link ConcurrentSessionControllerImpl}. The session controller is also registered
- * with the default {@link ProviderManager} (which is automatically registered during namespace configuration).
- *
- * @author Luke Taylor
- * @version $Id$
- */
-public class ConcurrentSessionsBeanDefinitionParser implements BeanDefinitionParser {
-
-    static final String ATT_EXPIRY_URL = "expired-url";
-    static final String ATT_SESSION_REGISTRY_ALIAS = "session-registry-alias";
-    static final String ATT_SESSION_REGISTRY_REF = "session-registry-ref";
-
-    public BeanDefinition parse(Element element, ParserContext pc) {
-        CompositeComponentDefinition compositeDef =
-            new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
-        pc.pushContainingComponent(compositeDef);
-
-        BeanDefinitionRegistry beanRegistry = pc.getRegistry();
-
-        String sessionRegistryId = element.getAttribute(ATT_SESSION_REGISTRY_REF);
-
-        if (!StringUtils.hasText(sessionRegistryId)) {
-            // Register an internal SessionRegistryImpl if no external reference supplied.
-            RootBeanDefinition sessionRegistry = new RootBeanDefinition(SessionRegistryImpl.class);
-            sessionRegistryId = pc.getReaderContext().registerWithGeneratedName(sessionRegistry);
-            pc.registerComponent(new BeanComponentDefinition(sessionRegistry, sessionRegistryId));
-        }
-
-        String registryAlias = element.getAttribute(ATT_SESSION_REGISTRY_ALIAS);
-        if (StringUtils.hasText(registryAlias)) {
-            beanRegistry.registerAlias(sessionRegistryId, registryAlias);
-        }
-
-        BeanDefinitionBuilder filterBuilder =
-                BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionFilter.class);
-        filterBuilder.addPropertyReference("sessionRegistry", sessionRegistryId);
-
-        Object source = pc.extractSource(element);
-        filterBuilder.getRawBeanDefinition().setSource(source);
-        filterBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
-
-        String expiryUrl = element.getAttribute(ATT_EXPIRY_URL);
-
-        if (StringUtils.hasText(expiryUrl)) {
-            WebConfigUtils.validateHttpRedirect(expiryUrl, pc, source);
-            filterBuilder.addPropertyValue("expiredUrl", expiryUrl);
-        }
-
-        pc.popAndRegisterContainingComponent();
-
-        return filterBuilder.getBeanDefinition();
-    }
-}

+ 16 - 7
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -37,6 +37,7 @@ import org.springframework.security.web.access.expression.WebExpressionVoter;
 import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.access.intercept.RequestKey;
+import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
 import org.springframework.security.web.authentication.concurrent.ConcurrentSessionFilter;
 import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
 import org.springframework.security.web.context.SecurityContextPersistenceFilter;
@@ -192,7 +193,7 @@ class HttpConfigurationBuilder {
 
     void createSessionManagementFilters() {
         Element sessionMgmtElt = DomUtils.getChildElementByTagName(httpElt, Elements.SESSION_MANAGEMENT);
-        Element sessionCtrlEt = null;
+        Element sessionCtrlElt = null;
 
         String sessionFixationAttribute = null;
         String invalidSessionUrl = null;
@@ -200,10 +201,10 @@ class HttpConfigurationBuilder {
         if (sessionMgmtElt != null) {
             sessionFixationAttribute = sessionMgmtElt.getAttribute(ATT_SESSION_FIXATION_PROTECTION);
             invalidSessionUrl = sessionMgmtElt.getAttribute(ATT_INVALID_SESSION_URL);
-            sessionCtrlEt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
+            sessionCtrlElt = DomUtils.getChildElementByTagName(sessionMgmtElt, Elements.CONCURRENT_SESSIONS);
 
-            if (sessionCtrlEt != null) {
-                createConcurrencyControlFilterAndSessionRegistry(sessionCtrlEt);
+            if (sessionCtrlElt != null) {
+                createConcurrencyControlFilterAndSessionRegistry(sessionCtrlElt);
             }
         }
 
@@ -214,22 +215,25 @@ class HttpConfigurationBuilder {
         boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
 
         BeanDefinitionBuilder sessionStrategy;
+        String concurrencyErrorUrl = null;
 
-        if (sessionCtrlEt != null) {
+        if (sessionCtrlElt != null) {
             assert sessionRegistryRef != null;
             sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(ConcurrentSessionControlAuthenticatedSessionStrategy.class);
             sessionStrategy.addConstructorArgValue(sessionRegistryRef);
 
-            String maxSessions = sessionCtrlEt.getAttribute("max-sessions");
+            String maxSessions = sessionCtrlElt.getAttribute("max-sessions");
 
             if (StringUtils.hasText(maxSessions)) {
                 sessionStrategy.addPropertyValue("maximumSessions", maxSessions);
             }
 
-            String exceptionIfMaximumExceeded = sessionCtrlEt.getAttribute("exception-if-maximum-exceeded");
+            String exceptionIfMaximumExceeded = sessionCtrlElt.getAttribute("error-if-maximum-exceeded");
 
             if (StringUtils.hasText(exceptionIfMaximumExceeded)) {
                 sessionStrategy.addPropertyValue("exceptionIfMaximumExceeded", exceptionIfMaximumExceeded);
+
+                concurrencyErrorUrl = sessionCtrlElt.getAttribute("error-url");
             }
         } else if (sessionFixationProtectionRequired || StringUtils.hasText(invalidSessionUrl)) {
             sessionStrategy = BeanDefinitionBuilder.rootBeanDefinition(DefaultSessionAuthenticationStrategy.class);
@@ -239,6 +243,11 @@ class HttpConfigurationBuilder {
         }
 
         BeanDefinitionBuilder sessionMgmtFilter = BeanDefinitionBuilder.rootBeanDefinition(SessionManagementFilter.class);
+        RootBeanDefinition failureHandler = new RootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
+        if (StringUtils.hasText(concurrencyErrorUrl)) {
+            failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", concurrencyErrorUrl);
+        }
+        sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler);
         sessionMgmtFilter.addConstructorArgValue(contextRepoRef);
         BeanDefinition strategyBean = sessionStrategy.getBeanDefinition();
 

+ 11 - 19
config/src/test/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParserTests.java

@@ -27,7 +27,6 @@ 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.ConcurrentLoginException;
 import org.springframework.security.authentication.concurrent.SessionRegistryImpl;
 import org.springframework.security.config.BeanIds;
 import org.springframework.security.config.PostProcessedMockUserDetailsService;
@@ -71,7 +70,6 @@ import org.springframework.security.web.context.HttpSessionSecurityContextReposi
 import org.springframework.security.web.context.SecurityContextPersistenceFilter;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
 import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
-import org.springframework.security.web.session.SessionAuthenticationStrategy;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.wrapper.SecurityContextHolderAwareRequestFilter;
 import org.springframework.util.ReflectionUtils;
@@ -704,33 +702,27 @@ public class HttpSecurityBeanDefinitionParserTests {
         assertSame(sessionRegistry, sessionRegistryFromFormLoginFilter);
     }
 
-    @Test(expected=ConcurrentLoginException.class)
+    @Test
     public void concurrentSessionMaxSessionsIsCorrectlyConfigured() throws Exception {
         setContext(
                 "<http auto-config='true'>" +
                 "    <session-management>" +
-                "        <concurrency-control max-sessions='2' exception-if-maximum-exceeded='true' />" +
+                "        <concurrency-control max-sessions='2' error-if-maximum-exceeded='true' error-url='/max-exceeded' />" +
                 "    </session-management>" +
                 "</http>"  + AUTH_PROVIDER_XML);
-        SessionAuthenticationStrategy seshStrategy = (SessionAuthenticationStrategy) FieldUtils.getFieldValue(
-                getFilter(SessionManagementFilter.class), "sessionStrategy");
+        SessionManagementFilter seshStrategy = (SessionManagementFilter) getFilter(SessionManagementFilter.class);
         UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("bob", "pass");
+        SecurityContextHolder.getContext().setAuthentication(auth);
         // Register 2 sessions and then check a third
 //        req.setSession(new MockHttpSession());
 //        auth.setDetails(new WebAuthenticationDetails(req));
-        try {
-            seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
-        } catch (ConcurrentLoginException e) {
-            fail("First login should be allowed");
-        }
-
-        try {
-            seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
-        } catch (ConcurrentLoginException e) {
-            fail("Second login should be allowed");
-        }
-
-        seshStrategy.onAuthentication(auth, new MockHttpServletRequest(), new MockHttpServletResponse());
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
+        assertNull(response.getRedirectedUrl());
+        seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
+        assertNull(response.getRedirectedUrl());
+        seshStrategy.doFilter(new MockHttpServletRequest(), response, new MockFilterChain());
+        assertEquals("/max-exceeded", response.getRedirectedUrl());
     }
 
     @Test