讲解完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)));
-
- BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
-
- String alias = element.getAttribute(ATT_ALIAS);
-
- checkForDeprecatedSessionControllerRef(element, pc);
- List<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();
- NamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();
-
- NodeList children = element.getChildNodes();
-
-
- for (int i = 0; i < children.getLength(); i++) {
- Node node = children.item(i);
- if (node instanceof Element) {
- Element providerElt = (Element)node;
-
-
- if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {
- providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF)));
- } else {
-
-
- 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);
-
- pc.registerBeanComponent(new BeanComponentDefinition(provider, id));
-
- 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));
-
-
- providerManagerBldr.addPropertyReference("authenticationEventPublisher", id);
-
- 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) {
-
- RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
- authProvider.setSource(parserContext.extractSource(element));
-
- Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);
-
- if (passwordEncoderElt != null) {
-
-
- PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, parserContext);
- authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());
-
- if (pep.getSaltSource() != null) {
- authProvider.getPropertyValues().addPropertyValue("saltSource", pep.getSaltSource());
- }
- }
-
- 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 {
-
- AbstractUserDetailsServiceBeanDefinitionParser parser = null;
- Element elt = null;
-
- 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));
- }
- }
-
- 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即可。
|