• Spring Security3中的-authentication-manager标签详解


    讲解完http标签的解析过程,authentication-manager标签解析部分就很容易理解了 
    authentication-manager标签在spring的配置文件中的定义一般如下 

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

    authentication-manager标签的解析类是: 
    org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser 
    具体解析方法parse的代码为 

     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 

     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有这么一段配置 

    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。 

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

    上面的标签如果用bean来定义,则可以完全由下面的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即可。

  • 相关阅读:
    C# 操作ini配置文件
    2015-2016跨年感想
    HTTP 状态码总结 (HTTP Status Codes)
    简单、精准、高效的使用搜索引擎,快速的找到你想要的结果
    ASP.NET前后台交互之JSON数据
    软件开发项目做需求分析的一点心得
    我是如何自学编程的“3遍读书法”
    C#中Request.ServerVariables详细说明及代理
    C#
    JavaScript如何计算两个日期间的时间差
  • 原文地址:https://www.cnblogs.com/xhj123/p/6193864.html
Copyright © 2020-2023  润新知