Spring Security3源码分析(3)-authentication-manager标签解析

讲解完http标签的解析过程,authentication-manager标签解析部分就很容易理解了 

authentication-manager标签在spring的配置文件中的定义一般如下 

Xml代码  

  1. <authentication-manager alias=“authenticationManager”>  
  2.     <authentication-provider user-service-ref=“userDetailsManager”/>  
  3. </authentication-manager>   

authentication-manager标签的解析类是: 

org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser 

具体解析方法parse的代码为 

Java代码  

  1. public BeanDefinition parse(Element element, ParserContext pc) {  
  2.         Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER),  
  3.                 “AuthenticationManager has already been registered!”);  
  4.         pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));  
  5.         //构造ProviderManager的BeanDefinition  
  6.         BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);  
  7.         //获取alias属性  
  8.         String alias = element.getAttribute(ATT_ALIAS);  
  9.         //检查session-controller-ref属性,提示通过标签<concurrent-session-control>取代  
  10.         checkForDeprecatedSessionControllerRef(element, pc);  
  11.         List<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();  
  12.         NamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();  
  13.         //获取authentication-manager的子节点  
  14.         NodeList children = element.getChildNodes();  
  15.         //循环节点,一般子节点主要是authentication-provider或者  
  16.          //ldap-authentication-provider  
  17.         for (int i = 0; i < children.getLength(); i++) {  
  18.             Node node = children.item(i);  
  19.             if (node instanceof Element) {  
  20.                 Element providerElt = (Element)node;  
  21.                 //判断子标签是否有ref属性,如果有,则直接将ref属性  
  22.                    //引用的bean id添加到providers集合中  
  23.                 if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {  
  24.                     providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF)));  
  25.                 } else {  
  26.                     //如果没有ref属性,则通过子标签的解析类完成标签解析  
  27.                        //如子标签:authentication-provider,解析过程在后面  
  28.                     BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc);  
  29.                     Assert.notNull(provider, “Parser for “ + providerElt.getNodeName() + ” returned a null bean definition”);  
  30.                     String id = pc.getReaderContext().generateBeanName(provider);  
  31.                     //注册provider的BeanDefinition  
  32.                     pc.registerBeanComponent(new BeanComponentDefinition(provider, id));  
  33.                     //添加注册过的bean到provider集合中  
  34.                     providers.add(new RuntimeBeanReference(id));  
  35.                 }  
  36.             }  
  37.         }  
  38.   
  39.         if (providers.isEmpty()) {  
  40.             providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));  
  41.         }  
  42.   
  43.         providerManagerBldr.addPropertyValue(“providers”, providers);  
  44.         //添加默认的事件发布类  
  45.         BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);  
  46.         String id = pc.getReaderContext().generateBeanName(publisher);  
  47.         pc.registerBeanComponent(new BeanComponentDefinition(publisher, id));  
  48.         //将事件发布类的bean注入到ProviderManager的  
  49.          //authenticationEventPublisher属性中  
  50.         providerManagerBldr.addPropertyReference(“authenticationEventPublisher”, id);  
  51.         //注册ProviderManager的bean  
  52.         pc.registerBeanComponent(  
  53.                 new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), BeanIds.AUTHENTICATION_MANAGER));  
  54.   
  55.         if (StringUtils.hasText(alias)) {  
  56.             pc.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);  
  57.             pc.getReaderContext().fireAliasRegistered(BeanIds.AUTHENTICATION_MANAGER, alias, pc.extractSource(element));  
  58.         }  
  59.   
  60.         pc.popAndRegisterContainingComponent();  
  61.   
  62.         return null;  
  63.     }  

通过上面的代码片段,能够知道authentication-manager标签解析的步骤是 


1.构造ProviderManager的BeanDefinition 

2.循环authentication-manager的子标签,构造provider的BeanDefinition,并添加到providers集合中 

3.将第2步的providers设置为ProviderManager的providers属性 

4.构造异常事件发布类DefaultAuthenticationEventPublisher的BeanDefinition,并设置为ProviderManager的属性authenticationEventPublisher 

5.通过registerBeanComponent方法完成bean的注册任务 

authentication-provider标签的解析类为 

org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser 

Java代码  

  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.     //首先构造DaoAuthenticationProvider的BeanDefinition  
  3.     RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);  
  4.     authProvider.setSource(parserContext.extractSource(element));  
  5.     //获取password-encoder子标签  
  6.     Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);  
  7.   
  8.     if (passwordEncoderElt != null) {  
  9.         //如果有password-encoder子标签,把解析任务交给  
  10.           //PasswordEncoderParser完成  
  11.         PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, parserContext);  
  12.         authProvider.getPropertyValues().addPropertyValue(“passwordEncoder”, pep.getPasswordEncoder());  
  13.         //如果有salt-source标签,将值注入到saltSource属性中  
  14.         if (pep.getSaltSource() != null) {  
  15.             authProvider.getPropertyValues().addPropertyValue(“saltSource”, pep.getSaltSource());  
  16.         }  
  17.     }  
  18.     //下面获取子标签user-service、jdbc-user-service、ldap-user-service  
  19.     Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);  
  20.     Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);  
  21.     Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);  
  22.   
  23.     String ref = element.getAttribute(ATT_USER_DETAILS_REF);  
  24.   
  25.     if (StringUtils.hasText(ref)) {  
  26.         if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {  
  27.             parserContext.getReaderContext().error(“The “ + ATT_USER_DETAILS_REF + ” attribute cannot be used in combination with child” +  
  28.                     “elements ‘” + Elements.USER_SERVICE + “‘, ‘” + Elements.JDBC_USER_SERVICE + “‘ or ‘” +  
  29.                     Elements.LDAP_USER_SERVICE + “‘”, element);  
  30.         }  
  31.     } else {  
  32.         // Use the child elements to create the UserDetailsService  
  33.         AbstractUserDetailsServiceBeanDefinitionParser parser = null;  
  34.         Element elt = null;  
  35.         //下面的if语句,主要是根据子标签的不同,选择子标签对应的解析器处理  
  36.         if (userServiceElt != null) {  
  37.             elt = userServiceElt;  
  38.             parser = new UserServiceBeanDefinitionParser();  
  39.         } else if (jdbcUserServiceElt != null) {  
  40.             elt = jdbcUserServiceElt;  
  41.             parser = new JdbcUserServiceBeanDefinitionParser();  
  42.         } else if (ldapUserServiceElt != null) {  
  43.             elt = ldapUserServiceElt;  
  44.             parser = new LdapUserServiceBeanDefinitionParser();  
  45.         } else {  
  46.             parserContext.getReaderContext().error(“A user-service is required”, element);  
  47.         }  
  48.   
  49.         parser.parse(elt, parserContext);  
  50.         ref = parser.getId();  
  51.         String cacheRef = elt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF);  
  52.   
  53.         if (StringUtils.hasText(cacheRef)) {  
  54.             authProvider.getPropertyValues().addPropertyValue(“userCache”new RuntimeBeanReference(cacheRef));  
  55.         }  
  56.     }  
  57.     //将解析后的bean id注入到userDetailsService属性中  
  58.     authProvider.getPropertyValues().addPropertyValue(“userDetailsService”new RuntimeBeanReference(ref));  
  59.     return authProvider;  
  60. }  

如果学习过acegi的配置,应该知道,acegi有这么一段配置 

Xml代码  

  1. <bean id=“authenticationManager” class=“org.acegisecurity.providers.ProviderManager”>  
  2.    <property name=“providers”>  
  3.       <list>  
  4.          <ref local=“daoAuthenticationProvider”/>  
  5.         <ref local=“anonymousAuthenticationProvider”/>  
  6.       </list>  
  7.    </property>  
  8. </bean>  

实际上authentication-manager标签所要达到的目标就是构造上面的bean。其中anonymousAuthenticationProvider是在http解析过程添加的。 

其实可以完全像acegi那样自定义每个bean。 

Xml代码  

  1. <authentication-manager alias=“authenticationManager”>  
  2.     <authentication-provider user-service-ref=“userDetailsManager”/>  
  3. </authentication-manager>  

上面的标签如果用bean来定义,则可以完全由下面的xml来替代。 

Xml代码  

  1. <bean id=“org.springframework.security.authenticationManager” class=“org.springframework.security.authentication.ProviderManager”>  
  2.         <property name=“authenticationEventPublisher” ref=“defaultAuthenticationEventPublisher”></property>  
  3.         <property name=“providers”>  
  4.             <list>  
  5.                 <ref local=“daoAuthenticationProvider”/>  
  6.                 <ref local=“anonymousAuthenticationProvider”/>  
  7.             </list>  
  8.         </property>  
  9.     </bean>  
  10.       
  11.     <bean id=“defaultAuthenticationEventPublisher” class=“org.springframework.security.authentication.DefaultAuthenticationEventPublisher”></bean>  
  12.       
  13.     <bean id=“anonymousAuthenticationProvider” class=“org.springframework.security.authentication.AnonymousAuthenticationProvider”>  
  14.         <property name=“key”><value>work</value></property>  
  15.     </bean>  
  16.       
  17.     <bean id=“daoAuthenticationProvider” class=“org.springframework.security.authentication.dao.DaoAuthenticationProvider”>  
  18.         <property name=“userDetailsService” ref=“userDetailsManager”></property>  
  19.     </bean>  

需要注意的是anonymousAuthenticationProvider的bean中,需要增加key属性。如果采用authentication-manager标签的方式,key虽然没有定义,在增加AnonymousAuthenticationFilter过滤器中,是通过java.security.SecureRandom.nextLong()来生成的。
 

显而易见,如果采用bean的方式来定义,非常复杂,而且需要了解底层的组装过程才行,不过能够提高更大的扩展性。采用authentication-manager标签的方式,很简洁,只需要提供UserDetailsService即可。

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/benjamin_whx/article/details/39204579
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞