Spring Security3源码分析-authentication-manager标签解析
程序员文章站
2022-03-02 11:02:06
...
讲解完http标签的解析过程,authentication-manager标签解析部分就很容易理解了
authentication-manager标签在spring的配置文件中的定义一般如下
authentication-manager标签的解析类是:
org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser
具体解析方法parse的代码为
通过上面的代码片段,能够知道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
如果学习过acegi的配置,应该知道,acegi有这么一段配置
实际上authentication-manager标签所要达到的目标就是构造上面的bean。其中anonymousAuthenticationProvider是在http解析过程添加的。
其实可以完全像acegi那样自定义每个bean。
上面的标签如果用bean来定义,则可以完全由下面的xml来替代。
需要注意的是anonymousAuthenticationProvider的bean中,需要增加key属性。如果采用authentication-manager标签的方式,key虽然没有定义,在增加AnonymousAuthenticationFilter过滤器中,是通过java.security.SecureRandom.nextLong()来生成的。
显而易见,如果采用bean的方式来定义,非常复杂,而且需要了解底层的组装过程才行,不过能够提高更大的扩展性。采用authentication-manager标签的方式,很简洁,只需要提供UserDetailsService即可。
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即可。
下一篇: html简介和常用标签说明
推荐阅读
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
spring源码深度解析— IOC 之 默认标签解析(上)
-
spring源码深度解析— IOC 之 默认标签解析(下)
-
Spring5源码分析(二):Spring IOC 源码解析
-
spring源码解析之ConfigurationClassPostProcessor分析
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
Spring源码分析(二)Spring怎么扩展解析xml接口的
-
spring源码深度解析— IOC 之 默认标签解析(上)
-
Spring源码解析之自定义标签的解析
-
Spring集成Mybatis源码分析(三)-配置文件初始化及XML解析