SEC-1847: Add authentication-manager-ref attribute to http and global-method-security namespace elements.

This commit is contained in:
Luke Taylor 2011-10-30 21:51:02 +00:00
parent bce4d81142
commit 2f67bb3032
9 changed files with 109 additions and 36 deletions

View File

@ -38,8 +38,13 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
private static final String ATT_ERASE_CREDENTIALS = "erase-credentials"; private static final String ATT_ERASE_CREDENTIALS = "erase-credentials";
public BeanDefinition parse(Element element, ParserContext pc) { public BeanDefinition parse(Element element, ParserContext pc) {
Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER), String id = element.getAttribute("id");
"AuthenticationManager has already been registered!");
if (!StringUtils.hasText(id)) {
Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER),
"Global AuthenticationManager has already been registered!");
id = BeanIds.AUTHENTICATION_MANAGER;
}
pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element))); pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));
BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class); BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
@ -72,9 +77,9 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
} else { } else {
BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc); BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc);
Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition"); Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition");
String id = pc.getReaderContext().generateBeanName(provider); String providerId = pc.getReaderContext().generateBeanName(provider);
pc.registerBeanComponent(new BeanComponentDefinition(provider, id)); pc.registerBeanComponent(new BeanComponentDefinition(provider, providerId));
providers.add(new RuntimeBeanReference(id)); providers.add(new RuntimeBeanReference(providerId));
} }
} }
} }
@ -91,16 +96,15 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
// Add the default event publisher // Add the default event publisher
BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class); BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);
String id = pc.getReaderContext().generateBeanName(publisher); String pubId = pc.getReaderContext().generateBeanName(publisher);
pc.registerBeanComponent(new BeanComponentDefinition(publisher, id)); pc.registerBeanComponent(new BeanComponentDefinition(publisher, pubId));
providerManagerBldr.addPropertyReference("authenticationEventPublisher", id); providerManagerBldr.addPropertyReference("authenticationEventPublisher", pubId);
pc.registerBeanComponent( pc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), id));
new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), BeanIds.AUTHENTICATION_MANAGER));
if (StringUtils.hasText(alias)) { if (StringUtils.hasText(alias)) {
pc.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias); pc.getRegistry().registerAlias(id, alias);
pc.getReaderContext().fireAliasRegistered(BeanIds.AUTHENTICATION_MANAGER, alias, pc.extractSource(element)); pc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element));
} }
pc.popAndRegisterContainingComponent(); pc.popAndRegisterContainingComponent();

View File

@ -19,8 +19,9 @@ import org.springframework.security.config.BeanIds;
*/ */
public class AuthenticationManagerFactoryBean implements FactoryBean<AuthenticationManager>, BeanFactoryAware { public class AuthenticationManagerFactoryBean implements FactoryBean<AuthenticationManager>, BeanFactoryAware {
private BeanFactory bf; private BeanFactory bf;
public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add an <authentication-manager> element " + public static final String MISSING_BEAN_ERROR_MESSAGE = "Did you forget to add a gobal <authentication-manager> element " +
"to your configuration (with child <authentication-provider> elements) ?"; "to your configuration (with child <authentication-provider> elements)? Alternatively you can use the " +
"authentication-manager-ref attribute on your <http> and <global-method-security> elements.";
public AuthenticationManager getObject() throws Exception { public AuthenticationManager getObject() throws Exception {
try { try {

View File

@ -23,7 +23,6 @@ import org.springframework.security.config.Elements;
import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean; import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;
import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.AnyRequestMatcher; import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils; import org.springframework.util.xml.DomUtils;
@ -41,6 +40,7 @@ import java.util.*;
public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
private static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class); private static final Log logger = LogFactory.getLog(HttpSecurityBeanDefinitionParser.class);
private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
private static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref"; private static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref";
static final String ATT_PATH_PATTERN = "pattern"; static final String ATT_PATH_PATTERN = "pattern";
static final String ATT_HTTP_METHOD = "method"; static final String ATT_HTTP_METHOD = "method";
@ -190,22 +190,32 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
} }
/** /**
* Creates the internal AuthenticationManager bean which uses the externally registered (global) one as * Creates the internal AuthenticationManager bean which uses either the externally registered (global) one as
* a parent. * a parent or the bean specified by "authentication-manager-ref".
* *
* All the providers registered by this &lt;http&gt; block will be registered with the internal * All the providers registered by this &lt;http&gt; block will be registered with the internal authentication
* authentication manager. * manager.
*/ */
private BeanReference createAuthenticationManager(Element element, ParserContext pc, private BeanReference createAuthenticationManager(Element element, ParserContext pc,
ManagedList<BeanReference> authenticationProviders) { ManagedList<BeanReference> authenticationProviders) {
String parentMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
BeanDefinitionBuilder authManager = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class); BeanDefinitionBuilder authManager = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
authManager.addConstructorArgValue(authenticationProviders); authManager.addConstructorArgValue(authenticationProviders);
authManager.addConstructorArgValue(new RootBeanDefinition(AuthenticationManagerFactoryBean.class));
RootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class); if (StringUtils.hasText(parentMgrRef)) {
clearCredentials.getPropertyValues().addPropertyValue("targetObject", new RootBeanDefinition(AuthenticationManagerFactoryBean.class)); authManager.addConstructorArgValue(new RuntimeBeanReference(parentMgrRef));
clearCredentials.getPropertyValues().addPropertyValue("targetMethod", "isEraseCredentialsAfterAuthentication"); } else {
authManager.addPropertyValue("eraseCredentialsAfterAuthentication", clearCredentials); RootBeanDefinition amfb = new RootBeanDefinition(AuthenticationManagerFactoryBean.class);
amfb.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String amfbId = pc.getReaderContext().generateBeanName(amfb);
pc.registerBeanComponent(new BeanComponentDefinition(amfb, amfbId));
RootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class);
clearCredentials.getPropertyValues().addPropertyValue("targetObject", new RuntimeBeanReference(amfbId));
clearCredentials.getPropertyValues().addPropertyValue("targetMethod", "isEraseCredentialsAfterAuthentication");
authManager.addConstructorArgValue(new RuntimeBeanReference(amfbId));
authManager.addPropertyValue("eraseCredentialsAfterAuthentication", clearCredentials);
}
authManager.getRawBeanDefinition().setSource(pc.extractSource(element)); authManager.getRawBeanDefinition().setSource(pc.extractSource(element));
BeanDefinition authMgrBean = authManager.getBeanDefinition(); BeanDefinition authMgrBean = authManager.getBeanDefinition();

View File

@ -69,6 +69,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
private final Log logger = LogFactory.getLog(getClass()); private final Log logger = LogFactory.getLog(getClass());
private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
private static final String ATT_ACCESS = "access"; private static final String ATT_ACCESS = "access";
private static final String ATT_EXPRESSION = "expression"; private static final String ATT_EXPRESSION = "expression";
private static final String ATT_ACCESS_MGR = "access-decision-manager-ref"; private static final String ATT_ACCESS_MGR = "access-decision-manager-ref";
@ -204,8 +205,10 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
accessManagerId = registerAccessManager(pc, jsr250Enabled, preInvocationVoter); accessManagerId = registerAccessManager(pc, jsr250Enabled, preInvocationVoter);
} }
String authMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
String runAsManagerId = element.getAttribute(ATT_RUN_AS_MGR); String runAsManagerId = element.getAttribute(ATT_RUN_AS_MGR);
BeanReference interceptor = registerMethodSecurityInterceptor(pc, accessManagerId, runAsManagerId, BeanReference interceptor = registerMethodSecurityInterceptor(pc, authMgrRef, accessManagerId, runAsManagerId,
metadataSource, afterInvocationProviders, source, useAspectJ); metadataSource, afterInvocationProviders, source, useAspectJ);
if (useAspectJ) { if (useAspectJ) {
@ -307,7 +310,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
return pointcutMap; return pointcutMap;
} }
private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String accessManagerId, private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String authMgrRef, String accessManagerId,
String runAsManagerId, BeanReference metadataSource, String runAsManagerId, BeanReference metadataSource,
List<BeanMetadataElement> afterInvocationProviders, Object source, boolean useAspectJ) { List<BeanMetadataElement> afterInvocationProviders, Object source, boolean useAspectJ) {
BeanDefinitionBuilder bldr = BeanDefinitionBuilder bldr =
@ -315,7 +318,9 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
AspectJMethodSecurityInterceptor.class : MethodSecurityInterceptor.class); AspectJMethodSecurityInterceptor.class : MethodSecurityInterceptor.class);
bldr.getRawBeanDefinition().setSource(source); bldr.getRawBeanDefinition().setSource(source);
bldr.addPropertyReference("accessDecisionManager", accessManagerId); bldr.addPropertyReference("accessDecisionManager", accessManagerId);
bldr.addPropertyValue("authenticationManager", new RootBeanDefinition(AuthenticationManagerDelegator.class)); RootBeanDefinition authMgr = new RootBeanDefinition(AuthenticationManagerDelegator.class);
authMgr.getConstructorArgumentValues().addGenericArgumentValue(authMgrRef);
bldr.addPropertyValue("authenticationManager", authMgr);
bldr.addPropertyValue("securityMetadataSource", metadataSource); bldr.addPropertyValue("securityMetadataSource", metadataSource);
if (StringUtils.hasText(runAsManagerId)) { if (StringUtils.hasText(runAsManagerId)) {
@ -367,13 +372,18 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
private AuthenticationManager delegate; private AuthenticationManager delegate;
private final Object delegateMonitor = new Object(); private final Object delegateMonitor = new Object();
private BeanFactory beanFactory; private BeanFactory beanFactory;
private final String authMgrBean;
AuthenticationManagerDelegator(String authMgrBean) {
this.authMgrBean = StringUtils.hasText(authMgrBean) ? authMgrBean : BeanIds.AUTHENTICATION_MANAGER;
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException { public Authentication authenticate(Authentication authentication) throws AuthenticationException {
synchronized(delegateMonitor) { synchronized(delegateMonitor) {
if (delegate == null) { if (delegate == null) {
Assert.state(beanFactory != null, "BeanFactory must be set to resolve " + BeanIds.AUTHENTICATION_MANAGER); Assert.state(beanFactory != null, "BeanFactory must be set to resolve " + authMgrBean);
try { try {
delegate = beanFactory.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class); delegate = beanFactory.getBean(authMgrBean, ProviderManager.class);
} catch (NoSuchBeanDefinitionException e) { } catch (NoSuchBeanDefinitionException e) {
if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) { if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) {
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER,

View File

@ -40,6 +40,10 @@ user-service-ref =
## A reference to a user-service (or UserDetailsService bean) Id ## A reference to a user-service (or UserDetailsService bean) Id
attribute user-service-ref {xsd:token} attribute user-service-ref {xsd:token}
authentication-manager-ref =
## A reference to an AuthenticationManager bean
attribute authentication-manager-ref {xsd:token}
data-source-ref = data-source-ref =
## A reference to a DataSource bean ## A reference to a DataSource bean
attribute data-source-ref {xsd:token} attribute data-source-ref {xsd:token}
@ -228,6 +232,9 @@ global-method-security.attlist &=
attribute mode {"aspectj"}? attribute mode {"aspectj"}?
global-method-security.attlist &= global-method-security.attlist &=
attribute metadata-source-ref {xsd:token}? attribute metadata-source-ref {xsd:token}?
global-method-security.attlist &=
authentication-manager-ref?
after-invocation-provider = after-invocation-provider =
## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security. ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security.
@ -319,9 +326,10 @@ http.attlist &=
## Prevents the jsessionid parameter from being added to rendered URLs. ## Prevents the jsessionid parameter from being added to rendered URLs.
attribute disable-url-rewriting {xsd:boolean}? attribute disable-url-rewriting {xsd:boolean}?
http.attlist &= http.attlist &=
## Exposes the list of filters defined by this configuration under this bean name in the application context. May be used by ## Exposes the list of filters defined by this configuration under this bean name in the application context.
name? name?
http.attlist &=
authentication-manager-ref?
access-denied-handler = access-denied-handler =
## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance. ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance.
@ -618,7 +626,9 @@ authentication-manager =
## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans. ## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans.
element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*} element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}
authman.attlist &= authman.attlist &=
## The alias you wish to use for the AuthenticationManager bean id?
authman.attlist &=
## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)
attribute alias {xsd:token}? attribute alias {xsd:token}?
authman.attlist &= authman.attlist &=
## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. ## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated.

View File

@ -98,6 +98,13 @@
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="authentication-manager-ref">
<xs:attribute name="authentication-manager-ref" use="required" type="xs:token">
<xs:annotation>
<xs:documentation>A reference to an AuthenticationManager bean</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="data-source-ref"> <xs:attributeGroup name="data-source-ref">
<xs:attribute name="data-source-ref" use="required" type="xs:token"> <xs:attribute name="data-source-ref" use="required" type="xs:token">
<xs:annotation> <xs:annotation>
@ -577,6 +584,11 @@
</xs:simpleType> </xs:simpleType>
</xs:attribute> </xs:attribute>
<xs:attribute name="metadata-source-ref" type="xs:token"/> <xs:attribute name="metadata-source-ref" type="xs:token"/>
<xs:attribute name="authentication-manager-ref" type="xs:token">
<xs:annotation>
<xs:documentation>A reference to an AuthenticationManager bean</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
@ -804,6 +816,11 @@
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation> <xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="authentication-manager-ref" type="xs:token">
<xs:annotation>
<xs:documentation>A reference to an AuthenticationManager bean</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup> </xs:attributeGroup>
<xs:attributeGroup name="access-denied-handler.attlist"> <xs:attributeGroup name="access-denied-handler.attlist">
@ -1357,9 +1374,14 @@
<xs:attributeGroup ref="security:authman.attlist"/> <xs:attributeGroup ref="security:authman.attlist"/>
</xs:complexType></xs:element> </xs:complexType></xs:element>
<xs:attributeGroup name="authman.attlist"> <xs:attributeGroup name="authman.attlist">
<xs:attribute name="id" type="xs:token">
<xs:annotation>
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="alias" type="xs:token"> <xs:attribute name="alias" type="xs:token">
<xs:annotation> <xs:annotation>
<xs:documentation>The alias you wish to use for the AuthenticationManager bean</xs:documentation> <xs:documentation>An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id)</xs:documentation>
</xs:annotation> </xs:annotation>
</xs:attribute> </xs:attribute>
<xs:attribute name="erase-credentials" type="xs:boolean"> <xs:attribute name="erase-credentials" type="xs:boolean">

View File

@ -54,6 +54,7 @@ import org.springframework.security.access.PermissionEvaluator
import org.springframework.security.core.Authentication import org.springframework.security.core.Authentication
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
import org.springframework.security.web.util.AntPathRequestMatcher import org.springframework.security.web.util.AntPathRequestMatcher
import org.springframework.security.authentication.AuthenticationManager
class MiscHttpConfigTests extends AbstractHttpConfigTests { class MiscHttpConfigTests extends AbstractHttpConfigTests {
def 'Minimal configuration parses'() { def 'Minimal configuration parses'() {
@ -679,6 +680,20 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
expect: expect:
getFilter(FilterSecurityInterceptor.class).accessDecisionManager.decisionVoters[3] instanceof WebExpressionVoter getFilter(FilterSecurityInterceptor.class).accessDecisionManager.decisionVoters[3] instanceof WebExpressionVoter
} }
def customAuthenticationManagerIsSupported() {
xml.http('auto-config': 'true', 'authentication-manager-ref': 'am')
xml.'b:bean'(id: 'am', 'class': MockAuthenticationManager.class.name)
createAppContext("")
expect:
getFilter(UsernamePasswordAuthenticationFilter.class).authenticationManager.parent instanceof MockAuthenticationManager
}
}
class MockAuthenticationManager implements AuthenticationManager {
Authentication authenticate(Authentication authentication) {
return null
}
} }
class MockPermissionEvaluator implements PermissionEvaluator { class MockPermissionEvaluator implements PermissionEvaluator {

View File

@ -69,10 +69,11 @@ class RememberMeConfigTests extends AbstractHttpConfigTests {
List logoutHandlers = FieldUtils.getFieldValue(getFilter(LogoutFilter.class), "handlers"); List logoutHandlers = FieldUtils.getFieldValue(getFilter(LogoutFilter.class), "handlers");
Map ams = appContext.getBeansOfType(ProviderManager.class); Map ams = appContext.getBeansOfType(ProviderManager.class);
ams.remove(BeanIds.AUTHENTICATION_MANAGER); ProviderManager am = (ams.values() as List).find { it instanceof ProviderManager && it.providers.size() == 2}
RememberMeAuthenticationProvider rmp = (ams.values() as List)[0].providers[1]; RememberMeAuthenticationProvider rmp = am.providers.find { it instanceof RememberMeAuthenticationProvider}
expect: expect:
rmp != null
5000 == FieldUtils.getFieldValue(rememberMeServices(), "tokenValiditySeconds") 5000 == FieldUtils.getFieldValue(rememberMeServices(), "tokenValiditySeconds")
// SEC-909 // SEC-909
logoutHandlers.size() == 2 logoutHandlers.size() == 2

View File

@ -22,7 +22,7 @@ import org.springframework.security.util.FieldUtils;
*/ */
public class AuthenticationManagerBeanDefinitionParserTests { public class AuthenticationManagerBeanDefinitionParserTests {
private static final String CONTEXT = private static final String CONTEXT =
"<authentication-manager>" + "<authentication-manager id='am'>" +
" <authentication-provider>" + " <authentication-provider>" +
" <user-service>" + " <user-service>" +
" <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" + " <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" +