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

View File

@ -0,0 +1,70 @@
package org.springframework.security.config;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.AuthenticationCredentialsNotFoundException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.annotation.BusinessService;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
/**
* @author Ben Alex
* @version $Id: InterceptMethodsBeanDefinitionDecoratorTests.java 2217 2007-10-27 00:45:30Z luke_t $
*/
public class AnnotationDrivenBeanDefinitionParserTests {
private ClassPathXmlApplicationContext appContext;
private BusinessService target;
@Before
public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/annotated-method-security.xml");
target = (BusinessService) appContext.getBean("target");
}
@After
public void closeAppContext() {
if (appContext != null) {
appContext.close();
}
SecurityContextHolder.clearContext();
}
@Test
public void targetShouldPreventProtectedMethodInvocationWithNoContext() {
try {
target.someUserMethod1();
fail("Expected AuthenticationCredentialsNotFoundException");
} catch (AuthenticationCredentialsNotFoundException expected) {
}
}
@Test
public void targetShouldAllowProtectedMethodInvocationWithCorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_USER")});
SecurityContextHolder.getContext().setAuthentication(token);
target.someUserMethod1();
}
@Test
public void targetShouldPreventProtectedMethodInvocationWithIncorrectRole() {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("Test", "Password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_SOMEOTHERROLE")});
SecurityContextHolder.getContext().setAuthentication(token);
try {
target.someAdminMethod();
fail("Expected AccessDeniedException");
} catch (AccessDeniedException expected) {
}
}
}

View File

@ -12,18 +12,21 @@ import org.springframework.security.intercept.method.aopalliance.MethodSecurityI
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element; import org.w3c.dom.Element;
/** /**
* Processes the top-level "annotation-driven" element. * Processes the top-level "annotation-driven" element.
* *
* @author Ben Alex * @author Ben Alex
* @version $Id$ * @version $Id$
*/ */
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser { class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
public static final String SECURITY_ANNOTATION_ATTRIBUTES_CLASS = "org.springframework.security.annotation.SecurityAnnotationAttributes"; 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) { public BeanDefinition parse(Element element, ParserContext parserContext) {
// Reflectively obtain the Annotation-based ObjectDefinitionSource. // 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. // 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) { } catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex); ReflectionUtils.handleReflectionException(ex);
} }
RootBeanDefinition securityAnnotations = new RootBeanDefinition(clazz); RootBeanDefinition securityAnnotations = new RootBeanDefinition(clazz);
parserContext.getRegistry().registerBeanDefinition(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES, securityAnnotations); parserContext.getRegistry().registerBeanDefinition(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES, securityAnnotations);
RootBeanDefinition methodDefinitionAttributes = new RootBeanDefinition(MethodDefinitionAttributes.class); RootBeanDefinition methodDefinitionAttributes = new RootBeanDefinition(MethodDefinitionAttributes.class);
methodDefinitionAttributes.getPropertyValues().addPropertyValue("attributes", new RuntimeBeanReference(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES)); methodDefinitionAttributes.getPropertyValues().addPropertyValue("attributes", new RuntimeBeanReference(BeanIds.SECURITY_ANNOTATION_ATTRIBUTES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_ATTRIBUTES, methodDefinitionAttributes); parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_ATTRIBUTES, methodDefinitionAttributes);
MethodSecurityInterceptorUtils.registerPostProcessorIfNecessary(parserContext.getRegistry());
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class); 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)); interceptor.getPropertyValues().addPropertyValue("objectDefinitionSource", new RuntimeBeanReference(BeanIds.METHOD_DEFINITION_ATTRIBUTES));
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor); parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_SECURITY_INTERCEPTOR, interceptor);
RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class); RootBeanDefinition advisor = new RootBeanDefinition(MethodDefinitionSourceAdvisor.class);
advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor); advisor.getConstructorArgumentValues().addGenericArgumentValue(interceptor);
parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor); parserContext.getRegistry().registerBeanDefinition(BeanIds.METHOD_DEFINITION_SOURCE_ADVISOR, advisor);
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
return null; return null;
} }
} }

View File

@ -3,18 +3,18 @@ package org.springframework.security.config;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; 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.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.AccessDecisionManager;
import org.springframework.security.AuthenticationManager; import org.springframework.security.AuthenticationManager;
import org.springframework.security.providers.ProviderManager; import org.springframework.security.providers.ProviderManager;
import org.springframework.security.userdetails.UserDetailsService; import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.vote.AffirmativeBased; import org.springframework.security.vote.AffirmativeBased;
import org.springframework.security.vote.AuthenticatedVoter; import org.springframework.security.vote.AuthenticatedVoter;
import org.springframework.security.vote.RoleVoter; 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.Arrays;
import java.util.Map; import java.util.Map;
@ -27,23 +27,17 @@ import java.util.Map;
* @version $Id$ * @version $Id$
*/ */
public abstract class ConfigUtils { public abstract class ConfigUtils {
static void registerAccessManagerIfNecessary(ConfigurableListableBeanFactory bf) { private static final Log logger = LogFactory.getLog(ConfigUtils.class);
if (bf.getBeanNamesForType(AccessDecisionManager.class).length > 0) {
return;
}
Assert.isInstanceOf(BeanDefinitionRegistry.class, bf, "Auto-registration of default AccessManager will " + static void registerDefaultAccessManagerIfNecessary(ParserContext parserContext) {
"only work with a BeanFactory which implements BeanDefinitionRegistry");
BeanDefinitionRegistry registry = (BeanDefinitionRegistry)bf; if (!parserContext.getRegistry().containsBeanDefinition(BeanIds.ACCESS_MANAGER)) {
if (!registry.containsBeanDefinition(BeanIds.ACCESS_MANAGER)) {
BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class); BeanDefinitionBuilder accessMgrBuilder = BeanDefinitionBuilder.rootBeanDefinition(AffirmativeBased.class);
accessMgrBuilder.addPropertyValue("decisionVoters", accessMgrBuilder.addPropertyValue("decisionVoters",
Arrays.asList(new Object[] {new RoleVoter(), new AuthenticatedVoter()})); Arrays.asList(new Object[] {new RoleVoter(), new AuthenticatedVoter()}));
BeanDefinition accessMgr = accessMgrBuilder.getBeanDefinition(); 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) { static UserDetailsService getUserDetailsService(ConfigurableListableBeanFactory bf) {
Map services = bf.getBeansOfType(UserDetailsService.class); Map services = bf.getBeansOfType(UserDetailsService.class);

View File

@ -73,6 +73,8 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision"; static final String ATT_SERVLET_API_PROVISION = "servlet-api-provision";
static final String DEF_SERVLET_API_PROVISION = "true"; static final String DEF_SERVLET_API_PROVISION = "true";
static final String ATT_ACCESS_MGR = "access-decision-manager";
public BeanDefinition parse(Element element, ParserContext parserContext) { public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class); RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class); RootBeanDefinition httpScif = new RootBeanDefinition(HttpSessionContextIntegrationFilter.class);
@ -139,6 +141,19 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource); 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"), parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"),
filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext); filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource, parserContext);

View File

@ -38,12 +38,6 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
private Log logger = LogFactory.getLog(getClass()); private Log logger = LogFactory.getLog(getClass());
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ConfigUtils.registerAccessManagerIfNecessary(beanFactory);
BeanDefinition securityInterceptor =
beanFactory.getBeanDefinition(BeanIds.FILTER_SECURITY_INTERCEPTOR);
ConfigUtils.configureSecurityInterceptor(beanFactory, securityInterceptor);
injectUserDetailsServiceIntoRememberMeServices(beanFactory); injectUserDetailsServiceIntoRememberMeServices(beanFactory);
injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory); injectAuthenticationEntryPointIntoExceptionTranslationFilter(beanFactory);
@ -55,8 +49,7 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) { private void injectUserDetailsServiceIntoRememberMeServices(ConfigurableListableBeanFactory beanFactory) {
try { try {
BeanDefinition rememberMeServices = BeanDefinition rememberMeServices = beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
beanFactory.getBeanDefinition(BeanIds.REMEMBER_ME_SERVICES);
rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService", rememberMeServices.getPropertyValues().addPropertyValue("userDetailsService",
ConfigUtils.getUserDetailsService(beanFactory)); ConfigUtils.getUserDetailsService(beanFactory));
} catch (NoSuchBeanDefinitionException e) { } catch (NoSuchBeanDefinitionException e) {

View File

@ -5,6 +5,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator; import org.springframework.aop.config.AbstractInterceptorDrivenBeanDefinitionDecorator;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder; 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.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator; import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext; 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.MethodDefinitionMap;
import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor; import org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor;
import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.DomUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@ -22,33 +25,48 @@ import java.util.List;
/** /**
* @author Luke Taylor * @author Luke Taylor
* @author Ben Alex * @author Ben Alex
* *
* @version $Id$ * @version $Id$
*/ */
public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDecorator { public class InterceptMethodsBeanDefinitionDecorator implements BeanDefinitionDecorator {
private BeanDefinitionDecorator delegate = new InternalInterceptMethodsBeanDefinitionDecorator(); private BeanDefinitionDecorator delegate = new InternalInterceptMethodsBeanDefinitionDecorator();
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { 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); 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 * This is the real class which does the work. We need acccess to the ParserContext in order to do bean
* post processor, * registration.
*/ */
class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator { class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator {
static final String ATT_CLASS = "class"; static final String ATT_CLASS = "class";
static final String ATT_METHOD = "method"; static final String ATT_METHOD = "method";
static final String ATT_ACCESS = "access"; 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) { protected BeanDefinition createInterceptorDefinition(Node node) {
Element interceptMethodsElt = (Element)node; Element interceptMethodsElt = (Element)node;
RootBeanDefinition interceptor = new RootBeanDefinition(MethodSecurityInterceptor.class); 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(); Element beanNode = (Element)interceptMethodsElt.getParentNode();
// Get the class from the parent bean... // Get the class from the parent bean...
String targetClassName = beanNode.getAttribute(ATT_CLASS); String targetClassName = beanNode.getAttribute(ATT_CLASS);
@ -57,7 +75,8 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
try { try {
targetClass = Thread.currentThread().getContextClassLoader().loadClass(targetClassName); targetClass = Thread.currentThread().getContextClassLoader().loadClass(targetClassName);
} catch (ClassNotFoundException e) { } 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 // Parse the included methods
@ -70,10 +89,10 @@ class InternalInterceptMethodsBeanDefinitionDecorator extends AbstractIntercepto
String accessConfig = protectmethodElt.getAttribute(ATT_ACCESS); String accessConfig = protectmethodElt.getAttribute(ATT_ACCESS);
attributeEditor.setAsText(accessConfig); 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"), // methodMap.addSecureMethod(targetClass, protectmethodElt.getAttribute("method"),
// (ConfigAttributeDefinition) attributeEditor.getValue()); // (ConfigAttributeDefinition) attributeEditor.getValue());
methodMap.addSecureMethod(protectmethodElt.getAttribute(ATT_METHOD), methodMap.addSecureMethod(protectmethodElt.getAttribute(ATT_METHOD),
(ConfigAttributeDefinition) attributeEditor.getValue()); (ConfigAttributeDefinition) attributeEditor.getValue());
} }

View File

@ -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));
}
}

View File

@ -16,29 +16,21 @@ import org.junit.*;
* @version $Id$ * @version $Id$
*/ */
public class InterceptMethodsBeanDefinitionDecoratorTests { public class InterceptMethodsBeanDefinitionDecoratorTests {
private static ClassPathXmlApplicationContext appContext; private ClassPathXmlApplicationContext appContext;
private TestBusinessBean target; 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 @Before
public void setUp() { public void loadContext() {
appContext = new ClassPathXmlApplicationContext("org/springframework/security/config/method-security.xml");
target = (TestBusinessBean) appContext.getBean("target"); target = (TestBusinessBean) appContext.getBean("target");
} }
@After @After
public void clearSecurityContext() { public void closeAppContext() {
if (appContext != null) {
appContext.close();
}
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
} }

View File

@ -185,7 +185,7 @@
to the Contact presented as a method argument. to the Contact presented as a method argument.
--> -->
<bean id="contactManagerSecurity" class="org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor"> <bean id="contactManagerSecurity" class="org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationManager" ref="_authenticationManager"/>
<property name="accessDecisionManager"> <property name="accessDecisionManager">
<ref local="businessAccessDecisionManager"/> <ref local="businessAccessDecisionManager"/>
</property> </property>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!-- <!--
- Application context containing business beans. - Application context containing business beans.
@ -9,7 +8,11 @@
- $Id$ - $Id$
--> -->
<beans> <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
@ -55,12 +58,12 @@
</bean> </bean>
<bean id="contactManagerTarget" class="sample.contact.ContactManagerBackend"> <bean id="contactManagerTarget" class="sample.contact.ContactManagerBackend">
<property name="contactDao"> <property name="contactDao">
<bean class="sample.contact.ContactDaoSpring"> <bean class="sample.contact.ContactDaoSpring">
<property name="dataSource"><ref local="dataSource"/></property> <property name="dataSource"><ref local="dataSource"/></property>
</bean> </bean>
</property> </property>
<property name="mutableAclService" ref="aclService"/> <property name="mutableAclService" ref="aclService"/>
</bean> </bean>
</beans> </beans>

View File

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<!-- <!--
- Application context containing authentication, channel - Application context containing authentication, channel
- security and web URI beans. - security and web URI beans.
@ -10,84 +8,55 @@
- $Id: applicationContext-acegi-security.xml 1425 2006-04-28 06:43:50Z benalex $ - $Id: applicationContext-acegi-security.xml 1425 2006-04-28 06:43:50Z benalex $
--> -->
<beans> <b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- ======================== FILTER CHAIN ======================= -->
<!-- if you wish to use channel security, add "channelProcessingFilter," in front <http auto-config="true" realm="Contacts Realm">
of "httpSessionContextIntegrationFilter" in the list below --> <intercept-url pattern="/index.jsp" access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url pattern="/hello.htm" access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url pattern="/switchuser.jsp" access="ROLE_SUPERVISOR"/>
<intercept-url pattern="/j_spring_security_switch_user" access="ROLE_SUPERVISOR"/>
<intercept-url pattern="/acegilogin.jsp*" access="ROLE_ANONYMOUS,ROLE_USER"/>
<intercept-url pattern="/**" access="ROLE_USER"/>
<form-login login-page="/acegilogin.jsp" authentication-failure-url="/acegilogin.jsp?login_error=1"/>
<logout logout-url="/index.jsp"/>
</http>
<!--
<bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy"> <bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource"> <property name="filterInvocationDefinitionSource">
<value><![CDATA[ <value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,switchUserProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor,switchUserProcessingFilter
]]></value> ]]></value>
</property> </property>
</bean> </bean>
<!-- ======================== AUTHENTICATION ======================= -->
<bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
<ref local="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="jdbcDaoImpl" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
<bean id="passwordEncoder" class="org.springframework.security.providers.encoding.Md5PasswordEncoder"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService"><ref local="jdbcDaoImpl"/></property>
<!-- <property name="userCache"><ref local="userCache"/></property> -->
<property name="passwordEncoder"><ref local="passwordEncoder"/></property>
</bean>
<!--
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>userCache</value>
</property>
</bean>
<bean id="userCache" class="org.springframework.security.providers.dao.cache.EhCacheBasedUserCache">
<property name="cache"><ref local="userCacheBackend"/></property>
</bean>
-->
<!-- Automatically receives AuthenticationEvent messages -->
<bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
<bean id="basicProcessingFilter" class="org.springframework.security.ui.basicauth.BasicProcessingFilter"> <bean id="basicProcessingFilter" class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager"><ref local="authenticationManager"/></property> <property name="authenticationManager"><ref local="authenticationManager"/></property>
<property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property> <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
</bean> </bean>
<bean id="basicProcessingFilterEntryPoint" class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint"> <bean id="basicProcessingFilterEntryPoint" class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName"><value>Contacts Realm</value></property> <property name="realmName" value="Contacts Realm"/>
</bean> </bean>
<bean id="anonymousProcessingFilter" class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter"> <bean id="anonymousProcessingFilter" class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
<property name="key"><value>foobar</value></property> <property name="key" value="foobar"/>
<property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean> </bean>
<bean id="anonymousAuthenticationProvider" class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider"> <bean id="anonymousAuthenticationProvider" class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key"><value>foobar</value></property> <property name="key" value="foobar"/>
</bean> </bean>
<bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"> <bean id="httpSessionContextIntegrationFilter" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
</bean>
<bean id="rememberMeProcessingFilter" class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter"> <bean id="rememberMeProcessingFilter" class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager"><ref local="authenticationManager"/></property> <property name="authenticationManager"><ref local="authenticationManager"/></property>
@ -96,15 +65,15 @@
<bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices"> <bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService"><ref local="jdbcDaoImpl"/></property> <property name="userDetailsService"><ref local="jdbcDaoImpl"/></property>
<property name="key"><value>springRocks</value></property> <property name="key" value="springRocks"/>
</bean> </bean>
<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider"> <bean id="rememberMeAuthenticationProvider" class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key"><value>springRocks</value></property> <property name="key" value="springRocks"/>
</bean> </bean>
<bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter"> <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
<constructor-arg value="/index.jsp"/> <!-- URL redirected to after logout --> <constructor-arg value="/index.jsp"/>
<constructor-arg> <constructor-arg>
<list> <list>
<ref bean="rememberMeServices"/> <ref bean="rememberMeServices"/>
@ -115,38 +84,6 @@
<bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter"/> <bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter"/>
<!-- ===================== HTTP CHANNEL REQUIREMENTS ==================== -->
<!-- You will need to uncomment the "Acegi Channel Processing Filter"
<filter-mapping> in web.xml for the following beans to be used -->
<bean id="channelProcessingFilter" class="org.springframework.security.securechannel.ChannelProcessingFilter">
<property name="channelDecisionManager"><ref local="channelDecisionManager"/></property>
<property name="filterInvocationDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
\A/j_spring_security_check.*\Z=REQUIRES_SECURE_CHANNEL
\A.*\Z=REQUIRES_INSECURE_CHANNEL
]]></value>
</property>
</bean>
<bean id="channelDecisionManager" class="org.springframework.security.securechannel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref local="secureChannelProcessor"/>
<ref local="insecureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor" class="org.springframework.security.securechannel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor" class="org.springframework.security.securechannel.InsecureChannelProcessor"/>
<!-- ===================== HTTP REQUEST SECURITY ==================== -->
<bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter"> <bean id="exceptionTranslationFilter" class="org.springframework.security.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property> <property name="authenticationEntryPoint"><ref local="authenticationProcessingFilterEntryPoint"/></property>
<property name="accessDeniedHandler"> <property name="accessDeniedHandler">
@ -169,18 +106,6 @@
<property name="forceHttps"><value>false</value></property> <property name="forceHttps"><value>false</value></property>
</bean> </bean>
<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<!-- Note the order that entries are placed against the objectDefinitionSource is critical.
The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last -->
<bean id="filterInvocationInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor"> <bean id="filterInvocationInterceptor" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property> <property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property> <property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
@ -199,14 +124,44 @@
</property> </property>
</bean> </bean>
-->
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source="dataSource"/>
</authentication-provider>
<!--
<bean id="jdbcDaoImpl" class="org.springframework.security.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="passwordEncoder" class="org.springframework.security.providers.encoding.Md5PasswordEncoder"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService"><ref local="jdbcDaoImpl"/></property>
<property name="passwordEncoder"><ref local="passwordEncoder"/></property>
</bean>
-->
<!-- Automatically receives AuthenticationEvent messages -->
<b:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener"/>
<b:bean id="httpRequestAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<b:property name="allowIfAllAbstainDecisions" value="false" />
<b:property name="decisionVoters">
<b:list>
<b:ref bean="roleVoter"/>
</b:list>
</b:property>
</b:bean>
<!-- Filter used to switch the user context. Note: the switch and exit url must be secured <!-- Filter used to switch the user context. Note: the switch and exit url must be secured
based on the role granted the ability to 'switch' to another user --> based on the role granted the ability to 'switch' to another user -->
<!-- In this example 'rod' has ROLE_SUPERVISOR that can switch to regular ROLE_USER(s) --> <!-- In this example 'rod' has ROLE_SUPERVISOR that can switch to regular ROLE_USER(s) -->
<bean id="switchUserProcessingFilter" class="org.springframework.security.ui.switchuser.SwitchUserProcessingFilter"> <b:bean id="switchUserProcessingFilter" class="org.springframework.security.ui.switchuser.SwitchUserProcessingFilter" autowire="byType">
<property name="userDetailsService" ref="jdbcDaoImpl" /> <b:property name="targetUrl" value="/spring-security-sample-contacts-filter/secure/index.htm"/>
<property name="switchUserUrl"><value>/j_spring_security_switch_user</value></property> </b:bean>
<property name="exitUserUrl"><value>/j_spring_security_exit_user</value></property>
<property name="targetUrl"><value>/spring-security-sample-contacts-filter/secure/index.htm</value></property>
</bean>
</beans> </b:beans>