講解完http標籤的解析過程,authentication-manager標籤解析部分就很容易理解了 
authentication-manager標籤在spring的配置檔案中的定義一般如下

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

authentication-manager標籤的解析類是: 
org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser 
具體解析方法parse的程式碼為

 public BeanDefinition parse(Element element, ParserContext pc) {
Assert.state(!pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER),
"AuthenticationManager has already been registered!");
pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));
//構造ProviderManager的BeanDefinition
BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
//獲取alias屬性
String alias = element.getAttribute(ATT_ALIAS);
//檢查session-controller-ref屬性,提示通過標籤<concurrent-session-control>取代
checkForDeprecatedSessionControllerRef(element, pc);
List<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();
NamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();
//獲取authentication-manager的子節點
NodeList children = element.getChildNodes();
//迴圈節點,一般子節點主要是authentication-provider或者
//ldap-authentication-provider
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node instanceof Element) {
Element providerElt = (Element)node;
//判斷子標籤是否有ref屬性,如果有,則直接將ref屬性
//引用的bean id新增到providers集合中
if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {
providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF)));
} else {
//如果沒有ref屬性,則通過子標籤的解析類完成標籤解析
//如子標籤:authentication-provider,解析過程在後面
BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc);
Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition");
String id = pc.getReaderContext().generateBeanName(provider);
//註冊provider的BeanDefinition
pc.registerBeanComponent(new BeanComponentDefinition(provider, id));
//添加註冊過的bean到provider集合中
providers.add(new RuntimeBeanReference(id));
}
}
} if (providers.isEmpty()) {
providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));
} providerManagerBldr.addPropertyValue("providers", providers);
//新增預設的事件釋出類
BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);
String id = pc.getReaderContext().generateBeanName(publisher);
pc.registerBeanComponent(new BeanComponentDefinition(publisher, id));
//將事件釋出類的bean注入到ProviderManager的
//authenticationEventPublisher屬性中
providerManagerBldr.addPropertyReference("authenticationEventPublisher", id);
//註冊ProviderManager的bean
pc.registerBeanComponent(
new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), BeanIds.AUTHENTICATION_MANAGER)); if (StringUtils.hasText(alias)) {
pc.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
pc.getReaderContext().fireAliasRegistered(BeanIds.AUTHENTICATION_MANAGER, alias, pc.extractSource(element));
} pc.popAndRegisterContainingComponent(); return null;
}

通過上面的程式碼片段,能夠知道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

 public BeanDefinition parse(Element element, ParserContext parserContext) {
//首先構造DaoAuthenticationProvider的BeanDefinition
RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
authProvider.setSource(parserContext.extractSource(element));
//獲取password-encoder子標籤
Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER); if (passwordEncoderElt != null) {
//如果有password-encoder子標籤,把解析任務交給
//PasswordEncoderParser完成
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, parserContext);
authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
//如果有salt-source標籤,將值注入到saltSource屬性中
if (pep.getSaltSource() != null) {
authProvider.getPropertyValues().addPropertyValue("saltSource", pep.getSaltSource());
}
}
//下面獲取子標籤user-service、jdbc-user-service、ldap-user-service
Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE); String ref = element.getAttribute(ATT_USER_DETAILS_REF); if (StringUtils.hasText(ref)) {
if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {
parserContext.getReaderContext().error("The " + ATT_USER_DETAILS_REF + " attribute cannot be used in combination with child" +
"elements '" + Elements.USER_SERVICE + "', '" + Elements.JDBC_USER_SERVICE + "' or '" +
Elements.LDAP_USER_SERVICE + "'", element);
}
} else {
// Use the child elements to create the UserDetailsService
AbstractUserDetailsServiceBeanDefinitionParser parser = null;
Element elt = null;
//下面的if語句,主要是根據子標籤的不同,選擇子標籤對應的解析器處理
if (userServiceElt != null) {
elt = userServiceElt;
parser = new UserServiceBeanDefinitionParser();
} else if (jdbcUserServiceElt != null) {
elt = jdbcUserServiceElt;
parser = new JdbcUserServiceBeanDefinitionParser();
} else if (ldapUserServiceElt != null) {
elt = ldapUserServiceElt;
parser = new LdapUserServiceBeanDefinitionParser();
} else {
parserContext.getReaderContext().error("A user-service is required", element);
} parser.parse(elt, parserContext);
ref = parser.getId();
String cacheRef = elt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF); if (StringUtils.hasText(cacheRef)) {
authProvider.getPropertyValues().addPropertyValue("userCache", new RuntimeBeanReference(cacheRef));
}
}
//將解析後的bean id注入到userDetailsService屬性中
authProvider.getPropertyValues().addPropertyValue("userDetailsService", new RuntimeBeanReference(ref));
return authProvider;
}

如果學習過acegi的配置,應該知道,acegi有這麼一段配置

 <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean>

實際上authentication-manager標籤所要達到的目標就是構造上面的bean。其中anonymousAuthenticationProvider是在http解析過程新增的。

其實可以完全像acegi那樣自定義每個bean。

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

上面的標籤如果用bean來定義,則可以完全由下面的xml來替代。

 <bean id="org.springframework.security.authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<property name="authenticationEventPublisher" ref="defaultAuthenticationEventPublisher"></property>
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
</list>
</property>
</bean> <bean id="defaultAuthenticationEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"></bean> <bean id="anonymousAuthenticationProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key"><value>work</value></property>
</bean> <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsManager"></property>
</bean>

需要注意的是anonymousAuthenticationProvider的bean中,需要增加key屬性。如果採用authentication-manager標籤的方式,key雖然沒有定義,在增加AnonymousAuthenticationFilter過濾器中,是通過java.security.SecureRandom.nextLong()來生成的。

顯而易見,如果採用bean的方式來定義,非常複雜,而且需要了解底層的組裝過程才行,不過能夠提高更大的擴充套件性。採用authentication-manager標籤的方式,很簡潔,只需要提供UserDetailsService即可。