security中配置多个AuthenticationManager

security中配置多个AuthenticationManager

基于spring-security4.2.x和security-oauth2.3.x

在使用Security配置Oauth2.0的时候需要多个authenticationManager来管理来自不同方向的认证管理,比如一个clientAuthenticationManager用来认证client_id和client_secret,配置另外一个authenticationManager来认证username和password

错误的配置方法:

<!-- authenticationManager for username and password -->
<!-- 不能用alias!! -->
<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>

<!--客户端访问认证器-->
<!-- authenticationManager for client_id and client_secret -->
<security:authentication-manager id="clientAuthenticationManager">
    <security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
</security:authentication-manager>

发现这样配置之后认证不能通过,全部都是以clientAuthenticationManager来认证管理。因为用id命名的clientAuthenticationManager会覆盖alias命名的authenticationManager,实践证明id会覆盖alias命名的authenticationManager

查看对<security:authentication-manager>标签解析的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<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();
    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.对<security:authentication-manager>标签都使用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>

<!-- authenticationManager for client_id and client_secret -->
<security:authentication-manager id="clientAuthenticationManager">
    <security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
</security:authentication-manager>

2.使用Bean方案创建:

<!-- authenticationManager for username and password -->
<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>


<!-- authenticationManager for client_id and client_secret -->
<security:authentication-manager id="clientAuthenticationManager">
    <security:authentication-provider user-service-ref="clientDetailsUserDetailsService"/>
</security:authentication-manager>

perfect!!

猜你喜欢

转载自blog.csdn.net/u013887008/article/details/80587542