diff --git a/core/src/main/java/org/springframework/security/config/ConfigUtils.java b/core/src/main/java/org/springframework/security/config/ConfigUtils.java new file mode 100644 index 0000000000..67d8ee1fe1 --- /dev/null +++ b/core/src/main/java/org/springframework/security/config/ConfigUtils.java @@ -0,0 +1,78 @@ +package org.springframework.security.config; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.security.AccessDecisionManager; +import org.springframework.security.AuthenticationManager; +import org.springframework.security.vote.AffirmativeBased; +import org.springframework.security.vote.AuthenticatedVoter; +import org.springframework.security.vote.RoleVoter; +import org.springframework.util.Assert; + +import java.util.Arrays; +import java.util.Map; + +/** + * @author Luke Taylor + * @version $Id$ + */ +public abstract class ConfigUtils { + public static final String DEFAULT_ACCESS_MANAGER_ID = "_accessManager"; + + static void registerAccessManagerIfNecessary(ConfigurableListableBeanFactory bf) { + if (bf.getBeanNamesForType(AccessDecisionManager.class).length > 0) { + return; + } + + Assert.isInstanceOf(BeanDefinitionRegistry.class, bf, " Auto-registration of default AccessManager will only work " + + "with a BeanFactory which implements BeanDefinitionRegistry"); + + BeanDefinitionRegistry registry = (BeanDefinitionRegistry)bf; + + if (!registry.containsBeanDefinition(DEFAULT_ACCESS_MANAGER_ID)) { + BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class); + accessMgrBuilder.addPropertyValue("decisionVoters", + Arrays.asList(new Object[] {new RoleVoter(), new AuthenticatedVoter()})); + BeanDefinition accessMgr = accessMgrBuilder.getBeanDefinition(); + + registry.registerBeanDefinition(DEFAULT_ACCESS_MANAGER_ID, accessMgr); + } + } + + static AuthenticationManager getAuthenticationManager(ConfigurableListableBeanFactory bf) { + Map authManagers = bf.getBeansOfType(AuthenticationManager.class); + + if (authManagers.size() == 0) { + throw new IllegalArgumentException("No AuthenticationManager registered. " + + "Make sure you have configured at least one AuthenticationProvider?"); + + } else if (authManagers.size() > 1) { + throw new IllegalArgumentException("More than one AuthenticationManager registered."); + } + + AuthenticationManager accessMgr = (AuthenticationManager) authManagers.values().toArray()[0]; + + return accessMgr; + } + + static void configureSecurityInterceptor(ConfigurableListableBeanFactory beanFactory, + BeanDefinition securityInterceptor) { + + ConfigUtils.registerAccessManagerIfNecessary(beanFactory); + + Map accessManagers = beanFactory.getBeansOfType(AccessDecisionManager.class); + + if (accessManagers.size() > 1) { + throw new IllegalArgumentException("More than one AccessDecisionManager registered. Please specify one " + + " using the TODO attribute."); + } + + AccessDecisionManager accessMgr = (AccessDecisionManager) accessManagers.values().toArray()[0]; + + securityInterceptor.getPropertyValues().addPropertyValue("accessDecisionManager", accessMgr); + securityInterceptor.getPropertyValues().addPropertyValue("authenticationManager", + getAuthenticationManager(beanFactory)); + } +} diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java index e323af7a14..464314dd55 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java @@ -88,7 +88,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource); // Again pick up auth manager - filterSecurityInterceptorBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); + //filterSecurityInterceptorBuilder.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"), filterChainMap, interceptorFilterInvDefSource); diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java index 01176e11ea..f1eab2e57b 100644 --- a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java +++ b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.core.OrderComparator; import org.springframework.core.Ordered; +import org.springframework.security.AccessDecisionManager; import org.springframework.security.AuthenticationManager; import org.springframework.security.context.HttpSessionContextIntegrationFilter; import org.springframework.security.ui.AuthenticationEntryPoint; @@ -15,7 +16,11 @@ import org.springframework.security.util.FilterChainProxy; import org.springframework.util.Assert; import javax.servlet.Filter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; /** * Responsible for tying up the HTTP security configuration - building ordered filter stack and linking up @@ -26,18 +31,29 @@ import java.util.*; */ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered { private Log logger = LogFactory.getLog(getClass()); + private AuthenticationManager authManager; public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { HttpSessionContextIntegrationFilter httpSCIF = (HttpSessionContextIntegrationFilter) beanFactory.getBean(HttpSecurityBeanDefinitionParser.DEFAULT_HTTP_SESSION_FILTER_ID); - AuthenticationManager authManager = - (AuthenticationManager) getBeanOfType(AuthenticationManager.class, beanFactory); + authManager = ConfigUtils.getAuthenticationManager(beanFactory); configureAuthenticationEntryPoint(beanFactory); + configureFilterSecurityInterceptor(beanFactory); + configureFilterChain(beanFactory); } + private void configureFilterSecurityInterceptor(ConfigurableListableBeanFactory beanFactory) { + ConfigUtils.registerAccessManagerIfNecessary(beanFactory); + + BeanDefinition securityInterceptor = + beanFactory.getBeanDefinition(HttpSecurityBeanDefinitionParser.DEFAULT_FILTER_SECURITY_INTERCEPTOR_ID); + + ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor); + } + /** * Selects the entry point that should be used in ExceptionTranslationFilter. Strategy is * @@ -127,6 +143,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor } public int getOrder() { - return 0; + return HIGHEST_PRECEDENCE; } } diff --git a/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java index 56ad3a98d8..a6b7d26bf7 100644 --- a/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java +++ b/core/src/main/java/org/springframework/security/config/InterceptMethodsBeanDefinitionDecorator.java @@ -1,24 +1,78 @@ package org.springframework.security.config; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor; -import org.springframework.security.intercept.method.MethodDefinitionMap; -import org.springframework.security.ConfigAttributeEditor; +import org.springframework.beans.factory.xml.BeanDefinitionDecorator; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Ordered; import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.security.ConfigAttributeEditor; +import org.springframework.security.intercept.method.MethodDefinitionMap; +import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor; import org.springframework.util.xml.DomUtils; -import org.w3c.dom.Node; import org.w3c.dom.Element; +import org.w3c.dom.Node; -import java.util.List; import java.util.Iterator; +import java.util.List; /** * @author Luke Taylor * @version $Id$ */ -public class InterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator { +public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDecorator { + private static final String POST_PROCESSOR_ID = "_interceptMethodsBeanfactoryPP"; + + private BeanDefinitionDecorator delegate = new InternalInterceptMethodsBeanDefinitionDecorator(); + + public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { + registerPostProcessorIfNecessary(parserContext.getRegistry()); + + return delegate.decorate(node, definition, parserContext); + } + + private void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry) { + if (registry.containsBeanDefinition(POST_PROCESSOR_ID)) { + return; + } + + registry.registerBeanDefinition(POST_PROCESSOR_ID, + new RootBeanDefinition(MethodSecurityConfigPostProcessor.class)); + } + + public static class MethodSecurityConfigPostProcessor implements BeanFactoryPostProcessor, Ordered { + + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + String[] interceptors = beanFactory.getBeanNamesForType(MethodSecurityInterceptor.class); + + for (int i=0; i < interceptors.length; i++) { + BeanDefinition interceptor = beanFactory.getBeanDefinition(interceptors[i]); + ConfigUtils.configureSecurityInterceptor(beanFactory, interceptor); + } + } + + public int getOrder() { + return HIGHEST_PRECEDENCE; + } + + } +} + +/** + * This is the real class which does the work. We need acccess to the ParserContext in order to register the + * post processor, + */ +class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator { + private Log logger = LogFactory.getLog(getClass()); + protected BeanDefinition createInterceptorDefinition(Node node) { Element interceptMethodsElt = (Element)node; RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class); @@ -53,8 +107,6 @@ public class InterceptMethodsBeanDefinitionDecorator extends AbstractInterceptor interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", methodMap); - interceptor.setAutowireMode(RootBeanDefinition.AUTOWIRE_BY_TYPE); - return interceptor; } } diff --git a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index c1f13bee21..7f477fcf90 100644 --- a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java @@ -15,7 +15,6 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport { registerBeanDefinitionParser("http", new HttpSecurityBeanDefinitionParser()); registerBeanDefinitionParser("user-service", new UserServiceBeanDefinitionParser()); registerBeanDefinitionParser("authentication-provider", new AuthenticationProviderBeanDefinitionParser()); - registerBeanDefinitionParser("autoconfig", new AutoConfigBeanDefinitionParser()); registerBeanDefinitionDecorator("intercept-methods", new InterceptMethodsBeanDefinitionDecorator()); registerBeanDefinitionDecorator("filter-chain-map", new FilterChainMapBeanDefinitionDecorator()); } diff --git a/core/src/test/resources/org/springframework/security/config/http-security.xml b/core/src/test/resources/org/springframework/security/config/http-security.xml index 616f87f682..15b921937f 100644 --- a/core/src/test/resources/org/springframework/security/config/http-security.xml +++ b/core/src/test/resources/org/springframework/security/config/http-security.xml @@ -6,8 +6,6 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> - - @@ -23,12 +21,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc - - \ No newline at end of file diff --git a/core/src/test/resources/org/springframework/security/config/method-security.xml b/core/src/test/resources/org/springframework/security/config/method-security.xml index a25c205b71..3ba61d90fd 100644 --- a/core/src/test/resources/org/springframework/security/config/method-security.xml +++ b/core/src/test/resources/org/springframework/security/config/method-security.xml @@ -6,8 +6,6 @@ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> - -