瀏覽代碼

SEC-879: Added required BeanPostProcessor to set SessionRegistry is set on namespace registered AbstractProcessingFilter and SessionFixationProtectionFilter when using custom ConcurrentSessionController
http://jira.springframework.org/browse/SEC-879.

Luke Taylor 17 年之前
父節點
當前提交
3ee8733261

+ 4 - 0
core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java

@@ -158,4 +158,8 @@ public class ConcurrentSessionControllerImpl implements ConcurrentSessionControl
     public void setSessionRegistry(SessionRegistry sessionRegistry) {
         this.sessionRegistry = sessionRegistry;
     }
+
+	public SessionRegistry getSessionRegistry() {
+		return sessionRegistry;
+	}
 }

+ 7 - 1
core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java

@@ -1,5 +1,6 @@
 package org.springframework.security.config;
 
+import org.springframework.beans.factory.support.RootBeanDefinition;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.beans.factory.config.BeanDefinition;
@@ -36,10 +37,15 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
             		BeanIds.CONCURRENT_SESSION_CONTROLLER, element);
         	authManager.getPropertyValues().addPropertyValue("sessionController", 
         			new RuntimeBeanReference(sessionControllerRef));
+            RootBeanDefinition sessionRegistryInjector = new RootBeanDefinition(SessionRegistryInjectionBeanPostProcessor.class);
+            sessionRegistryInjector.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+            sessionRegistryInjector.getConstructorArgumentValues().addGenericArgumentValue(sessionControllerRef);
+        	
+        	parserContext.getRegistry().registerBeanDefinition(BeanIds.SESSION_REGISTRY_INJECTION_POST_PROCESSOR, sessionRegistryInjector);
         }
 
         parserContext.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
 
         return null;
-    }
+    }    
 }

+ 1 - 0
core/src/main/java/org/springframework/security/config/BeanIds.java

@@ -18,6 +18,7 @@ public abstract class BeanIds {
     static final String CONTEXT_SOURCE_SETTING_POST_PROCESSOR = "_contextSettingPostProcessor";
     static final String ENTRY_POINT_INJECTION_POST_PROCESSOR = "_entryPointInjectionBeanPostProcessor";
     static final String USER_DETAILS_SERVICE_INJECTION_POST_PROCESSOR = "_userServiceInjectionPostProcessor";
+    static final String SESSION_REGISTRY_INJECTION_POST_PROCESSOR = "_sessionRegistryInjectionPostProcessor";    
     static final String FILTER_CHAIN_POST_PROCESSOR = "_filterChainProxyPostProcessor";
     static final String FILTER_LIST = "_filterChainList";
 

+ 94 - 0
core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java

@@ -0,0 +1,94 @@
+package org.springframework.security.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.security.concurrent.ConcurrentSessionController;
+import org.springframework.security.concurrent.ConcurrentSessionControllerImpl;
+import org.springframework.security.concurrent.SessionRegistry;
+import org.springframework.security.ui.AbstractProcessingFilter;
+import org.springframework.security.ui.SessionFixationProtectionFilter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Registered by the <tt>AuthenticationManagerBeanDefinitionParser</tt> if an external 
+ * ConcurrentSessionController is set (and hence an external SessionRegistry). 
+ * Its responsibility is to set the SessionRegistry on namespace-registered beans which require access
+ * to it.
+ * <p>
+ * It will attempt to read the registry directly from the registered controller. If that fails, it will look in
+ * the application context for a registered SessionRegistry bean.
+ * 
+ * See SEC-879. 
+ * 
+ * @author Luke Taylor
+ * @since 2.0.3
+ */
+class SessionRegistryInjectionBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
+    private final Log logger = LogFactory.getLog(getClass());
+    private ListableBeanFactory beanFactory;
+    private SessionRegistry sessionRegistry;
+    private final String controllerBeanName;
+
+    SessionRegistryInjectionBeanPostProcessor(String controllerBeanName) {
+    	this.controllerBeanName = controllerBeanName;
+    }
+    
+	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+		if (BeanIds.FORM_LOGIN_FILTER.equals(beanName) || 
+				BeanIds.OPEN_ID_FILTER.equals(beanName)) {
+			((AbstractProcessingFilter) bean).setSessionRegistry(getSessionRegistry());
+		} else if (BeanIds.SESSION_FIXATION_PROTECTION_FILTER.equals(beanName)) {
+			((SessionFixationProtectionFilter)bean).setSessionRegistry(getSessionRegistry());
+		}
+
+		return bean;
+	}	
+	
+	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+		return bean;
+	}
+	
+	private SessionRegistry getSessionRegistry() {
+		if (sessionRegistry != null) {
+			return sessionRegistry;
+		}
+		
+		logger.info("Attempting to read SessionRegistry from registered ConcurrentSessionController bean");
+		
+		ConcurrentSessionController controller = (ConcurrentSessionController) beanFactory.getBean(controllerBeanName);
+		
+		if (controller instanceof ConcurrentSessionControllerImpl) {
+			sessionRegistry = ((ConcurrentSessionControllerImpl)controller).getSessionRegistry();
+			
+			return sessionRegistry;
+		}
+
+		logger.info("ConcurrentSessionController is not a standard implementation. SessionRegistry could not be read from it. Looking for it in the context.");
+		
+		List sessionRegs = new ArrayList(beanFactory.getBeansOfType(SessionRegistry.class).values());
+		
+		if (sessionRegs.size() == 0) {			
+			throw new SecurityConfigurationException("concurrent-session-controller-ref was set but no SessionRegistry could be obtained from the application context.");
+		}
+		
+		if (sessionRegs.size() > 1) {
+			logger.warn("More than one SessionRegistry instance in application context. Possible configuration errors may result.");
+		}
+		
+		sessionRegistry = (SessionRegistry) sessionRegs.get(0);
+		
+		return sessionRegistry;
+	}
+
+	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+		this.beanFactory = (ListableBeanFactory) beanFactory;
+	}
+}

+ 66 - 0
core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java

@@ -0,0 +1,66 @@
+package org.springframework.security.config;
+
+import static org.junit.Assert.*;
+
+import org.junit.After;
+import org.junit.Test;
+import org.springframework.context.support.AbstractXmlApplicationContext;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.concurrent.ConcurrentSessionController;
+import org.springframework.security.util.FieldUtils;
+import org.springframework.security.util.InMemoryXmlApplicationContext;
+
+/**
+ * 
+ * @author Luke Taylor
+ */
+public class SessionRegistryInjectionBeanPostProcessorTests {
+    private AbstractXmlApplicationContext appContext;
+    
+    @After
+    public void closeAppContext() {
+        if (appContext != null) {
+            appContext.close();
+            appContext = null;
+        }
+    }
+
+    private void setContext(String context) {
+        appContext = new InMemoryXmlApplicationContext(context);
+    }
+
+    @Test
+    public void sessionRegistryIsSetOnFiltersWhenUsingCustomControllerWithInternalRegistryBean() throws Exception {
+        setContext(
+                "<http auto-config='true'/>" +
+                "<b:bean id='sc' class='org.springframework.security.concurrent.ConcurrentSessionControllerImpl'>" +
+                "  <b:property name='sessionRegistry'>" +
+                "    <b:bean class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
+                "  </b:property>" +
+                "</b:bean>" +
+                "<authentication-manager alias='authManager' session-controller-ref='sc'/>" + 
+                HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML);
+    	assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER), "sessionRegistry"));
+    	assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.FORM_LOGIN_FILTER), "sessionRegistry"));    	
+    }
+    
+    @Test
+    public void sessionRegistryIsSetOnFiltersWhenUsingCustomControllerWithNonStandardController() throws Exception {
+        setContext(
+                "<http auto-config='true'/>" +
+                "<b:bean id='sc' class='org.springframework.security.config.SessionRegistryInjectionBeanPostProcessorTests$MockConcurrentSessionController'/>" +
+                "<b:bean id='sessionRegistry' class='org.springframework.security.concurrent.SessionRegistryImpl'/>" +
+                "<authentication-manager alias='authManager' session-controller-ref='sc'/>" + 
+                HttpSecurityBeanDefinitionParserTests.AUTH_PROVIDER_XML);
+    	assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.SESSION_FIXATION_PROTECTION_FILTER), "sessionRegistry"));
+    	assertNotNull(FieldUtils.getFieldValue(appContext.getBean(BeanIds.FORM_LOGIN_FILTER), "sessionRegistry"));    	
+    }
+    
+    public static class MockConcurrentSessionController implements ConcurrentSessionController {
+		public void checkAuthenticationAllowed(Authentication request) throws AuthenticationException {
+		}
+		public void registerSuccessfulAuthentication(Authentication authentication) {
+		}    	
+    }
+}