講解完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