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即可。


Original url: Access
Created at: 2019-08-08 16:20:34
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论