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

View File

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

View File

@ -23,7 +23,6 @@ import org.springframework.security.config.Elements;
import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@ -41,6 +40,7 @@ import java.util.*;
public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
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";
static final String ATT_PATH_PATTERN = "pattern";
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
* a parent.
* Creates the internal AuthenticationManager bean which uses either the externally registered (global) one as
* 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
* authentication manager.
* All the providers registered by this &lt;http&gt; block will be registered with the internal authentication
* manager.
*/
private BeanReference createAuthenticationManager(Element element, ParserContext pc,
ManagedList<BeanReference> authenticationProviders) {
String parentMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
BeanDefinitionBuilder authManager = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
authManager.addConstructorArgValue(authenticationProviders);
authManager.addConstructorArgValue(new RootBeanDefinition(AuthenticationManagerFactoryBean.class));
RootBeanDefinition clearCredentials = new RootBeanDefinition(MethodInvokingFactoryBean.class);
clearCredentials.getPropertyValues().addPropertyValue("targetObject", new RootBeanDefinition(AuthenticationManagerFactoryBean.class));
clearCredentials.getPropertyValues().addPropertyValue("targetMethod", "isEraseCredentialsAfterAuthentication");
authManager.addPropertyValue("eraseCredentialsAfterAuthentication", clearCredentials);
if (StringUtils.hasText(parentMgrRef)) {
authManager.addConstructorArgValue(new RuntimeBeanReference(parentMgrRef));
} else {
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));
BeanDefinition authMgrBean = authManager.getBeanDefinition();

View File

@ -69,6 +69,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
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_EXPRESSION = "expression";
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);
}
String authMgrRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
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);
if (useAspectJ) {
@ -307,7 +310,7 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
return pointcutMap;
}
private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String accessManagerId,
private BeanReference registerMethodSecurityInterceptor(ParserContext pc, String authMgrRef, String accessManagerId,
String runAsManagerId, BeanReference metadataSource,
List<BeanMetadataElement> afterInvocationProviders, Object source, boolean useAspectJ) {
BeanDefinitionBuilder bldr =
@ -315,7 +318,9 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
AspectJMethodSecurityInterceptor.class : MethodSecurityInterceptor.class);
bldr.getRawBeanDefinition().setSource(source);
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);
if (StringUtils.hasText(runAsManagerId)) {
@ -367,13 +372,18 @@ public class GlobalMethodSecurityBeanDefinitionParser implements BeanDefinitionP
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
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 {
synchronized(delegateMonitor) {
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 {
delegate = beanFactory.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
delegate = beanFactory.getBean(authMgrBean, ProviderManager.class);
} catch (NoSuchBeanDefinitionException e) {
if (BeanIds.AUTHENTICATION_MANAGER.equals(e.getBeanName())) {
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
attribute user-service-ref {xsd:token}
authentication-manager-ref =
## A reference to an AuthenticationManager bean
attribute authentication-manager-ref {xsd:token}
data-source-ref =
## A reference to a DataSource bean
attribute data-source-ref {xsd:token}
@ -228,6 +232,9 @@ global-method-security.attlist &=
attribute mode {"aspectj"}?
global-method-security.attlist &=
attribute metadata-source-ref {xsd:token}?
global-method-security.attlist &=
authentication-manager-ref?
after-invocation-provider =
## 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.
attribute disable-url-rewriting {xsd:boolean}?
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?
http.attlist &=
authentication-manager-ref?
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.
@ -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.
element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*}
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}?
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.

View File

@ -98,6 +98,13 @@
</xs:annotation>
</xs:attribute>
</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:attribute name="data-source-ref" use="required" type="xs:token">
<xs:annotation>
@ -577,6 +584,11 @@
</xs:simpleType>
</xs:attribute>
<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>
@ -804,6 +816,11 @@
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
</xs:annotation>
</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 name="access-denied-handler.attlist">
@ -1357,9 +1374,14 @@
<xs:attributeGroup ref="security:authman.attlist"/>
</xs:complexType></xs:element>
<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: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:attribute>
<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.web.access.expression.DefaultWebSecurityExpressionHandler
import org.springframework.security.web.util.AntPathRequestMatcher
import org.springframework.security.authentication.AuthenticationManager
class MiscHttpConfigTests extends AbstractHttpConfigTests {
def 'Minimal configuration parses'() {
@ -679,6 +680,20 @@ class MiscHttpConfigTests extends AbstractHttpConfigTests {
expect:
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 {

View File

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

View File

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