基于spring-security4.2.x和security-oauth2.3.x
在使用Security配置Oauth2.0的时候需要多个authenticationManager来管理来自不同方向的认证管理,比如一个clientAuthenticationManager用来认证client_id和client_secret,配置另外一个authenticationManager来认证username和password
错误的配置方法:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service id="userDetailsService">
<security:user name="admin" password="111111" authorities="ROLE_USER" />
<security:user name="user" password="111111" authorities="ROLE_USER" />
security:user-service>
security:authentication-provider>
security:authentication-manager>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
security:authentication-manager>
发现这样配置之后认证不能通过,全部都是以clientAuthenticationManager
来认证管理。因为用id
命名的clientAuthenticationManager
会覆盖alias
命名的authenticationManager
,实践证明id
会覆盖alias
命名的authenticationManager
查看对
标签解析的parser:AuthenticationManagerBeanDefinitionParser.java
:
public BeanDefinition parse(Element element, ParserContext pc) {
String id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)) {
pc.getReaderContext().warning(
"Overriding globally registered AuthenticationManager",
pc.extractSource(element));
}
id = BeanIds.AUTHENTICATION_MANAGER;
}
pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(),
pc.extractSource(element)));
BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder
.rootBeanDefinition(ProviderManager.class);
//取出alias
String alias = element.getAttribute(ATT_ALIAS);
List providers = new ManagedList();
NamespaceHandlerResolver resolver = pc.getReaderContext()
.getNamespaceHandlerResolver();
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
Element providerElt = (Element) node;
if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {
if (providerElt.getAttributes().getLength() > 1) {
pc.getReaderContext().error(
"authentication-provider element cannot be used with other attributes "
+ "when using 'ref' attribute",
pc.extractSource(element));
}
NodeList providerChildren = providerElt.getChildNodes();
for (int j = 0; j < providerChildren.getLength(); j++) {
if (providerChildren.item(j) instanceof Element) {
pc.getReaderContext().error(
"authentication-provider element cannot have child elements when used "
+ "with 'ref' attribute",
pc.extractSource(element));
}
}
providers.add(new RuntimeBeanReference(providerElt
.getAttribute(ATT_REF)));
}
else {
BeanDefinition provider = resolver.resolve(
providerElt.getNamespaceURI()).parse(providerElt, pc);
Assert.notNull(provider, "Parser for " + providerElt.getNodeName()
+ " returned a null bean definition");
String providerId = pc.getReaderContext().generateBeanName(provider);
pc.registerBeanComponent(new BeanComponentDefinition(provider,
providerId));
providers.add(new RuntimeBeanReference(providerId));
}
}
}
if (providers.isEmpty()) {
providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));
}
providerManagerBldr.addConstructorArgValue(providers);
if ("false".equals(element.getAttribute(ATT_ERASE_CREDENTIALS))) {
providerManagerBldr.addPropertyValue("eraseCredentialsAfterAuthentication",
false);
}
// Add the default event publisher
BeanDefinition publisher = new RootBeanDefinition(
DefaultAuthenticationEventPublisher.class);
String pubId = pc.getReaderContext().generateBeanName(publisher);
pc.registerBeanComponent(new BeanComponentDefinition(publisher, pubId));
providerManagerBldr.addPropertyReference("authenticationEventPublisher", pubId);
pc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr
.getBeanDefinition(), id));
//为id设置别名,实际上还是同一个authenticationManger实例
if (StringUtils.hasText(alias)) {
pc.getRegistry().registerAlias(id, alias);
pc.getReaderContext().fireAliasRegistered(id, alias,
pc.extractSource(element));
}
if (!BeanIds.AUTHENTICATION_MANAGER.equals(id)) {
pc.getRegistry().registerAlias(id, BeanIds.AUTHENTICATION_MANAGER);
pc.getReaderContext().fireAliasRegistered(id, BeanIds.AUTHENTICATION_MANAGER,
pc.extractSource(element));
}
pc.popAndRegisterContainingComponent();
return null;
}
1.对
标签都使用id
来指定authenticationManger的名称,这样就创建了两个不同的实例:
<security:authentication-manager id="authenticationManager" erase-credentials="true">
<security:authentication-provider>
<security:user-service id="userDetailsService">
<security:user name="admin" password="111111" authorities="ROLE_USER" />
<security:user name="user" password="111111" authorities="ROLE_USER" />
security:user-service>
security:authentication-provider>
security:authentication-manager>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
security:authentication-manager>
2.使用Bean方案创建:
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsManager"/>
bean>
list>
constructor-arg>
bean>
<security:user-service id="userDetailsManager">
<security:user name="admin" password="111111" authorities="ROLE_USER" />
<security:user name="user" password="111111" authorities="ROLE_USER" />
security:user-service>
<security:authentication-manager id="clientAuthenticationManager">
<security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
security:authentication-manager>
perfect!!