Spring Security3源码分析(3)-authentication-manager标签解析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

讲解完http标签的解析过程,authentication-manager标签解析部分就很容易理解了

authentication-manager标签在spring的配置文件中的定义一般如下

Xml代码

  1. <**authentication-manager alias=“authenticationManager”>**
  2. <**authentication-provider user-service-ref=“userDetailsManager”/>**
  3. </**authentication-manager**>

authentication-manager标签的解析类是:

org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser

具体解析方法parse的代码为

Java代码

  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属性,提示通过标签取代
  10. checkForDeprecatedSessionControllerRef(element, pc);
  11. List providers = new ManagedList();
  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. if (providers.isEmpty()) {
  39. providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));
  40. }
  41. providerManagerBldr.addPropertyValue(“providers”, providers);
  42. //添加默认的事件发布类
  43. BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);
  44. String id = pc.getReaderContext().generateBeanName(publisher);
  45. pc.registerBeanComponent(new BeanComponentDefinition(publisher, id));
  46. //将事件发布类的bean注入到ProviderManager的
  47. //authenticationEventPublisher属性中
  48. providerManagerBldr.addPropertyReference(“authenticationEventPublisher”, id);
  49. //注册ProviderManager的bean
  50. pc.registerBeanComponent(
  51. new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), BeanIds.AUTHENTICATION_MANAGER));
  52. if (StringUtils.hasText(alias)) {
  53. pc.getRegistry().registerAlias(BeanIds.AUTHENTICATION_MANAGER, alias);
  54. pc.getReaderContext().fireAliasRegistered(BeanIds.AUTHENTICATION_MANAGER, alias, pc.extractSource(element));
  55. }
  56. pc.popAndRegisterContainingComponent();
  57. return null;
  58. }

通过上面的代码片段,能够知道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

Java代码

  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. if (passwordEncoderElt != null) {
  8. //如果有password-encoder子标签,把解析任务交给
  9. //PasswordEncoderParser完成
  10. PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, parserContext);
  11. authProvider.getPropertyValues().addPropertyValue(“passwordEncoder”, pep.getPasswordEncoder());
  12. //如果有salt-source标签,将值注入到saltSource属性中
  13. if (pep.getSaltSource() != null) {
  14. authProvider.getPropertyValues().addPropertyValue(“saltSource”, pep.getSaltSource());
  15. }
  16. }
  17. //下面获取子标签user-service、jdbc-user-service、ldap-user-service
  18. Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);
  19. Element jdbcUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
  20. Element ldapUserServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
  21. String ref = element.getAttribute(ATT_USER_DETAILS_REF);
  22. if (StringUtils.hasText(ref)) {
  23. if (userServiceElt != null || jdbcUserServiceElt != null || ldapUserServiceElt != null) {
  24. parserContext.getReaderContext().error(“The “ + ATT_USER_DETAILS_REF + ” attribute cannot be used in combination with child” +
  25. “elements ‘” + Elements.USER_SERVICE + “‘, ‘” + Elements.JDBC_USER_SERVICE + “‘ or ‘” +
  26. Elements.LDAP_USER_SERVICE + “‘”, element);
  27. }
  28. } else {
  29. // Use the child elements to create the UserDetailsService
  30. AbstractUserDetailsServiceBeanDefinitionParser parser = null;
  31. Element elt = null;
  32. //下面的if语句,主要是根据子标签的不同,选择子标签对应的解析器处理
  33. if (userServiceElt != null) {
  34. elt = userServiceElt;
  35. parser = new UserServiceBeanDefinitionParser();
  36. } else if (jdbcUserServiceElt != null) {
  37. elt = jdbcUserServiceElt;
  38. parser = new JdbcUserServiceBeanDefinitionParser();
  39. } else if (ldapUserServiceElt != null) {
  40. elt = ldapUserServiceElt;
  41. parser = new LdapUserServiceBeanDefinitionParser();
  42. } else {
  43. parserContext.getReaderContext().error(“A user-service is required”, element);
  44. }
  45. parser.parse(elt, parserContext);
  46. ref = parser.getId();
  47. String cacheRef = elt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF);
  48. if (StringUtils.hasText(cacheRef)) {
  49. authProvider.getPropertyValues().addPropertyValue(“userCache”, new RuntimeBeanReference(cacheRef));
  50. }
  51. }
  52. //将解析后的bean id注入到userDetailsService属性中
  53. authProvider.getPropertyValues().addPropertyValue(“userDetailsService”, new RuntimeBeanReference(ref));
  54. return authProvider;
  55. }

如果学习过acegi的配置,应该知道,acegi有这么一段配置

Xml代码

  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。

Xml代码

  1. <**authentication-manager alias=“authenticationManager”>**
  2. <**authentication-provider user-service-ref=“userDetailsManager”/>**
  3. </**authentication-manager**>

上面的标签如果用bean来定义,则可以完全由下面的xml来替代。

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. <**bean id=“defaultAuthenticationEventPublisher” class=“org.springframework.security.authentication.DefaultAuthenticationEventPublisher”></bean**>
  11. <**bean id=“anonymousAuthenticationProvider” class=“org.springframework.security.authentication.AnonymousAuthenticationProvider”>**
  12. <**property name=“key”><value**>work</**value></property>**
  13. </**bean**>
  14. <**bean id=“daoAuthenticationProvider” class=“org.springframework.security.authentication.dao.DaoAuthenticationProvider”>**
  15. <**property name=“userDetailsService” ref=“userDetailsManager”></property**>
  16. </**bean**>

需要注意的是anonymousAuthenticationProvider的bean中,需要增加key属性。如果采用authentication-manager标签的方式,key虽然没有定义,在增加AnonymousAuthenticationFilter过滤器中,是通过java.security.SecureRandom.nextLong()来生成的。

显而易见,如果采用bean的方式来定义,非常复杂,而且需要了解底层的组装过程才行,不过能够提高更大的扩展性。采用authentication-manager标签的方式,很简洁,只需要提供UserDetailsService即可。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Security3源码分析(3)-authentication-manager标签解析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏