`
Dead_knight
  • 浏览: 1202315 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:240625
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:49056
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:237060
社区版块
存档分类
最新评论

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

阅读更多
讲解完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即可。
分享到:
评论
2 楼 a723319751 2016-01-10  
mark.
1 楼 lrghktk 2015-10-22  
      

相关推荐

    Spring Security 3 源码分析文档

    通过阅读《Spring Security3.pdf》和《spring security3 源码分析.pdf》这两份文档,你可以对Spring Security 3的内部工作机制有更深入的理解,从而更好地在项目中运用这个强大的安全框架。同时,这也会帮助你在面临...

    spring-security-samples-contacts-2.0.4

    3. **配置Spring Security** - **WebSecurityConfigurerAdapter**: 自定义安全配置,包括HTTP安全设置、认证和授权规则。 - `@EnableWebSecurity`: 标记配置类启用Spring Security。 - `http.authorizeRequests()...

    spring-boot-security

    3. **使用Spring Security的API**:在你的控制器或其他服务中,可以注入`Authentication`和`SecurityContextHolder`来获取当前用户的认证信息。 4. **错误处理**:Spring Security默认提供了一些错误页面,但你可以...

    spring security源码分析.pdf

    ### Spring Security 源码分析知识...以上内容涵盖了 Spring Security 3 的源码分析中几个关键点的具体内容。通过对这些内容的深入学习和理解,可以更好地掌握 Spring Security 的工作原理及其在实际项目中的应用技巧。

    SpringSecurity-Jar包

    在这个"SpringSecurity-Jar包"中,包含了三个核心的jar文件:spring-security-config-4.2.3.RELEASE.jar、spring-security-core-4.2.3.RELEASE.jar以及spring-security-web-4.2.3.RELEASE.jar,它们各自承载了Spring...

    Packt Spring Security 3(2010-05).pdf

    ### Spring Security 3:安全防护全面指南 #### 引言 《Packt Spring Security 3》是一本由Packt出版社于2010年5月发行的专业书籍,旨在帮助开发者们掌握Spring Security 3框架的核心功能和技术细节。本书由Peter ...

    SpringSecurity学习总结源代码

    SpringSecurity是Java开发中用于构建安全Web应用的框架,它提供了强大的身份验证、...在学习过程中,分析提供的源代码和示例将有助于深入理解SpringSecurity的工作原理,并能帮助你在实际项目中有效地应用这些知识。

    spring-security源代码

    这个压缩包文件"spring-security-parent-2.0.4"是Spring Security的2.0.4版本,是一个Eclipse项目,适合开发者直接导入到Eclipse工作空间进行学习和分析。 1. **Spring Security架构**: Spring Security的架构...

    springsecurity所有jar包

    5. **spring-security-taglibs**:包含Spring Security的JSTL标签库,如`sec:authorize`,使得在JSP页面上实现安全控制变得简单。 6. **spring-security-ldap**:这个模块支持与LDAP(轻量级目录访问协议)服务器...

    spring-security-web-3 source code

    《深入剖析Spring Security Web 3源码...总结,通过对`spring-security-web-3`源码的分析,开发者不仅可以掌握Spring Security的内在工作原理,还能提升解决实际安全问题的能力,为构建更安全的Web应用提供坚实的基础。

    SpringSecurity源码

    本教程配套源码旨在帮助开发者深入理解SpringSecurity的工作原理,通过实践提升在安全领域的技能。 在SpringSecurity中,核心概念主要包括以下几个部分: 1. **过滤器链**:SpringSecurity的核心是基于过滤器的Web...

    spring security3.0所需要的最精简的jar包

    通过在Spring配置文件中定义`&lt;http&gt;`、`&lt;authentication-manager&gt;`等元素,或使用`@EnableGlobalAuthentication`、`@Secured`等注解,可以方便地配置安全策略。 4. **spring-security-ldap-3.1.3.RELEASE.jar**:这...

    spring security 项目配置源码

    - 通过源码分析,了解Spring Security的拦截器如何工作,以及如何自定义安全规则。 - 查看`SecurityConfig`类,这是Spring Security的核心配置,它扩展了`WebSecurityConfigurerAdapter`并覆盖了一些关键方法。 -...

    SpringSecurity笔记2-SpringSecurity命名空间

    在SpringSecurity中,`&lt;authentication-manager&gt;`命名空间处理用户的认证逻辑。它可以包含`&lt;authentication-provider&gt;`元素,后者定义了认证提供者,如JDBCAuthenticationProvider或DaoAuthenticationProvider,用于...

    SpringSecurity笔记,编程不良人笔记

    3. **SpringBoot整合SpringSecurity** - `spring-boot-starter-security`依赖:SpringBoot项目中添加此依赖即可自动配置SpringSecurity。 - 自定义登录页面:通过设置`loginPage`和`loginProcessingUrl`属性,可以...

    spring-security-3.1.x.zip 源码下载

    Spring Security 是一个强大的、高度可定制的身份验证和访问控制框架,广泛用于构建安全的Java Web应用程序。这个"spring-security-3.1.x.zip"压缩包包含的是Spring Security 3.1版本的源代码,这对于开发者深入理解...

    springsecurity角色和权限

    当用户尝试登录时,Spring Security创建一个`Authentication`对象,其中包含了用户的身份信息。这个对象包含了`Principal`(通常是`UserDetails`对象)和`Authorities`(角色列表)。 6. **Access Decision ...

    spring-security-3.1.0.RC3

    《Spring Security 3.1.0.RC3:企业级安全框架详解》 Spring Security是Java平台上广泛使用的安全框架,其3.1.0.RC3版本为开发者提供了强大而灵活的安全控制,使得构建安全的Web应用变得简单易行。本篇文章将深入...

    spring-security-oauth2与spring-security-web 3.1.2 源码

    在这个源码分析中,我们将深入探讨`spring-security-web 3.1.2`和`spring-security-oauth2`这两个关键组件。 首先,`spring-security-web`是Spring Security的核心库,它提供了Web应用程序的基本安全功能,如HTTP...

    初识 Spring Security - v1.1.pdf

    通过以上详细解析,可以看出Spring Security是一个非常强大且灵活的安全框架,它不仅能够满足基本的认证需求,还提供了丰富的高级特性,如权限管理、Remember-Me功能、session管理等。这对于构建安全可靠的企业级...

Global site tag (gtag.js) - Google Analytics