diff --git a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java b/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java index b770d230b6..4435509701 100644 --- a/core/src/main/java/org/springframework/security/concurrent/ConcurrentSessionControllerImpl.java +++ b/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; + } } diff --git a/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java index 7176bbc3ca..4533d8cc0e 100644 --- a/core/src/main/java/org/springframework/security/config/AuthenticationManagerBeanDefinitionParser.java +++ b/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; - } + } } diff --git a/core/src/main/java/org/springframework/security/config/BeanIds.java b/core/src/main/java/org/springframework/security/config/BeanIds.java index 7b029581d2..6160bcea63 100644 --- a/core/src/main/java/org/springframework/security/config/BeanIds.java +++ b/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"; diff --git a/core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java b/core/src/main/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessor.java new file mode 100644 index 0000000000..8cb1744796 --- /dev/null +++ b/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 AuthenticationManagerBeanDefinitionParser 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. + *
+ * 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;
+ }
+}
diff --git a/core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java b/core/src/test/java/org/springframework/security/config/SessionRegistryInjectionBeanPostProcessorTests.java
new file mode 100644
index 0000000000..f0c1554e0c
--- /dev/null
+++ b/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(
+ "