1
0
mirror of synced 2026-08-04 17:27:13 +00:00

Moved configuration of security interceptors with access and authentication manangers from post processing stage to bean creation stage.

This commit is contained in:
Luke Taylor
2007-12-23 16:40:29 +00:00
parent 46c99d1991
commit 5f1eea42fc
11 changed files with 227 additions and 250 deletions
@@ -12,18 +12,21 @@ import org.springframework.security.intercept.method.aopalliance.MethodSecurityI
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Processes the top-level "annotation-driven" element.
*
*
* @author Ben Alex
* @version $Id$
*/
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
public static final String SECURITY_ANNOTATION_ATTRIBUTES_CLASS = "org.springframework.security.annotation.SecurityAnnotationAttributes";
private static final String ATT_ACCESS_MGR = "access-decision-manager";
public BeanDefinition parse(Element element, ParserContext parserContext) {
// Reflectively obtain the Annotation-based ObjectDefinitionSource.
// Reflection is used to avoid a compile-time dependency on SECURITY_ANNOTATION_ATTRIBUTES_CLASS, as this parser is in the Java 4 project whereas the dependency is in the Tiger project.
@@ -34,26 +37,38 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
} catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
RootBeanDefinition securityAnnotations = new RootBeanDefinition(clazz);
parserContext.getRegistry().registerBeanDefinition(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES, securityAnnotations);
RootBeanDefinition methodDefinitionAttributes = new RootBeanDefinition(MethodDefinitionAttributes.class);
methodDefinitionAttributes.getPropertyValues().addPropertyValue("attributes", new RuntimeBeanReference(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_ATTRIBUTES, methodDefinitionAttributes);
MethodSecurityInterceptorUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
accessManagerId = BeanIds.ACCESS_MANAGER;
}
interceptor.getPropertyValues().addPropertyValue("accessDecisionManager",
new RuntimeBeanReference(accessManagerId));
interceptor.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.METHOD_DEFINITION_ATTRIBUTES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor);
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null;
}
}
@@ -3,18 +3,18 @@ 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.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.AccessDecisionManager;
import org.springframework.security.AuthenticationManager;
import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter;
import org.springframework.util.Assert;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Arrays;
import java.util.Map;
@@ -27,23 +27,17 @@ import java.util.Map;
* @version $Id$
*/
public abstract class ConfigUtils {
static void registerAccessManagerIfNecessary(ConfigurableListableBeanFactory bf) {
if (bf.getBeanNamesForType(AccessDecisionManager.class).length > 0) {
return;
}
private static final Log logger = LogFactory.getLog(ConfigUtils.class);
Assert.isInstanceOf(BeanDefinitionRegistry.class, bf, "Auto-registration of default AccessManager will " +
"only work with a BeanFactory which implements BeanDefinitionRegistry");
static void registerDefaultAccessManagerIfNecessary(ParserContext parserContext) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry)bf;
if (!registry.containsBeanDefinition(BeanIds.ACCESS_MANAGER)) {
if (!parserContext.getRegistry().containsBeanDefinition(BeanIds.ACCESS_MANAGER)) {
BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class);
accessMgrBuilder.addPropertyValue("decisionVoters",
Arrays.asList(new Object[] {new RoleVoter(), new AuthenticatedVoter()}));
BeanDefinition accessMgr = accessMgrBuilder.getBeanDefinition();
registry.registerBeanDefinition(BeanIds.ACCESS_MANAGER, accessMgr);
parserContext.getRegistry().registerBeanDefinition(BeanIds.ACCESS_MANAGER, accessMgr);
}
}
@@ -66,32 +60,6 @@ public abstract class ConfigUtils {
}
/**
* Supplies the BeanDefinition for an instance of AbstractSecurityInterceptor with the default
* AccessDecisionManager and AuthenticationManager.
*
* @param beanFactory
* @param securityInterceptor
*/
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));
}
static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
Map services = bf.getBeansOfType(UserDetailsService.class);
@@ -73,6 +73,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
static final String DEF_SERVLET_API_PROVISION = "true";
static final String ATT_ACCESS_MGR = "access-decision-manager";
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
@@ -139,6 +141,19 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource);
// Set up the access manager and authentication mananger references for http
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
accessManagerId = BeanIds.ACCESS_MANAGER;
}
filterSecurityInterceptorBuilder.addPropertyValue("accessDecisionManager",
new RuntimeBeanReference(accessManagerId));
filterSecurityInterceptorBuilder.addPropertyValue("authenticationManager",
ConfigUtils.registerProviderManagerIfNecessary(parserContext));
parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"),
filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext);
@@ -38,12 +38,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
private Log logger = LogFactory.getLog(getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigUtils.registerAccessManagerIfNecessary(beanFactory);
BeanDefinition securityInterceptor =
beanFactory.getBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR);
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
injectUserDetailsServiceIntoRememberMeServices(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
@@ -55,8 +49,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
try {
BeanDefinition rememberMeServices =
beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
BeanDefinition rememberMeServices = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(beanFactory));
} catch (NoSuchBeanDefinitionException e) {
@@ -5,6 +5,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
@@ -13,6 +14,8 @@ 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.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -22,33 +25,48 @@ import java.util.List;
/**
* @author Luke Taylor
* @author Ben Alex
*
*
* @version $Id$
*/
public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDecorator {
private BeanDefinitionDecorator delegate = new InternalInterceptMethodsBeanDefinitionDecorator();
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
MethodSecurityInterceptorUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
ConfigUtils.registerProviderManagerIfNecessary(parserContext);
ConfigUtils.registerDefaultAccessManagerIfNecessary(parserContext);
return delegate.decorate(node, definition, parserContext);
}
}
/**
* This is the real class which does the work. We need acccess to the ParserContext in order to register the
* post processor,
* This is the real class which does the work. We need acccess to the ParserContext in order to do bean
* registration.
*/
class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator {
static final String ATT_CLASS = "class";
static final String ATT_METHOD = "method";
static final String ATT_ACCESS = "access";
private Log logger = LogFactory.getLog(getClass());
private static final String ATT_ACCESS_MGR = "access-decision-manager";
private Log logger = LogFactory.getLog(getClass());
protected BeanDefinition createInterceptorDefinition(Node node) {
Element interceptMethodsElt = (Element)node;
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class);
String accessManagerId = interceptMethodsElt.getAttribute(ATT_ACCESS_MGR);
if (!StringUtils.hasText(accessManagerId)) {
accessManagerId = BeanIds.ACCESS_MANAGER;
}
interceptor.getPropertyValues().addPropertyValue("accessDecisionManager",
new RuntimeBeanReference(accessManagerId));
interceptor.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
Element beanNode = (Element)interceptMethodsElt.getParentNode();
// Get the class from the parent bean...
String targetClassName = beanNode.getAttribute(ATT_CLASS);
@@ -57,7 +75,8 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
try {
targetClass = Thread.currentThread().getContextClassLoader().loadClass(targetClassName);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Couldn't load class " + targetClassName, e);
logger.error("Couldn't load class " + targetClassName);
throw new SecurityConfigurationException("Couldn't load class " + targetClassName);
}
// Parse the included methods
@@ -70,10 +89,10 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
String accessConfig = protectmethodElt.getAttribute(ATT_ACCESS);
attributeEditor.setAsText(accessConfig);
// TODO: We want to use just the method names, but MethodDefinitionMap won't work that way.
// TODO: We want to use just the method names, but MethodDefinitionMap won't work that way.
// methodMap.addSecureMethod(targetClass, protectmethodElt.getAttribute("method"),
// (ConfigAttributeDefinition) attributeEditor.getValue());
methodMap.addSecureMethod(protectmethodElt.getAttribute(ATT_METHOD),
methodMap.addSecureMethod(protectmethodElt.getAttribute(ATT_METHOD),
(ConfigAttributeDefinition) attributeEditor.getValue());
}
@@ -1,53 +0,0 @@
package org.springframework.security.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
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.core.Ordered;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
/**
* Provides convenience methods supporting method security configuration.
*
* @author Ben Alex
* @author Luke Taylor
*
*/
abstract class MethodSecurityInterceptorUtils {
private 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;
}
}
/**
* Causes a BeanFactoryPostProcessor to be registered that will ensure all MethodSecurityInterceptor
* instances are properly configured with an AccessDecisionManager etc.
*
* @param registry to register the BeanPostProcessorWith
*/
public static void registerPostProcessorIfNecessary(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(BeanIds.INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR)) {
return;
}
registry.registerBeanDefinition(BeanIds.INTERCEPT_METHODS_BEAN_FACTORY_POST_PROCESSOR,
new RootBeanDefinition(MethodSecurityInterceptorUtils.MethodSecurityConfigPostProcessor.class));
}
}
@@ -16,29 +16,21 @@ import org.junit.*;
* @version $Id$
*/
public class InterceptMethodsBeanDefinitionDecoratorTests {
private static ClassPathXmlApplicationContext appContext;
private ClassPathXmlApplicationContext appContext;
private TestBusinessBean target;
@BeforeClass
public static void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/method-security.xml");
}
@AfterClass
public static void closeAppContext() {
if (appContext != null) {
appContext.close();
}
}
@Before
public void setUp() {
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/method-security.xml");
target = (TestBusinessBean) appContext.getBean("target");
}
@After
public void clearSecurityContext() {
public void closeAppContext() {
if (appContext != null) {
appContext.close();
}
SecurityContextHolder.clearContext();
}