Spring Security3中的-authentication-manager標籤詳解

講解完http標籤的解析過程,authentication-manager標籤解析部分就很容易理解了 
authentication-manager標籤在spring的配置文件中的定義通常以下 java

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

authentication-manager標籤的解析類是: 
org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser 
具體解析方法parse的代碼爲 node

 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 spring

 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有這麼一段配置 session

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解析過程添加的。 dom


其實能夠徹底像acegi那樣自定義每一個bean。 ide

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便可。ui

相關文章
相關標籤/搜索