Moved configuration of security interceptors with access and authentication manangers from post processing stage to bean creation stage.
This commit is contained in:
+24
-9
@@ -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);
|
||||
|
||||
|
||||
+15
@@ -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);
|
||||
|
||||
|
||||
+1
-8
@@ -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) {
|
||||
|
||||
+27
-8
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
-53
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
+7
-15
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user