Spring Security4.0.3源码分析之authentication-manager标签解析

最近在学习安全框架spring Security,想弄清楚其中实现的具体步骤,于是下定决心,研究一下Spring Security源码,这篇博客的目的是想把学习过程记录下来。学习过程中主要参考了http://dead-knight.iteye.com/category/220917大神的博客,然后在其基础上,进行更详细的说明

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

<authentication-manager alias="authenticationManager">  
    <authentication-provider user-service-ref="userDetailsManager"/>  
</authentication-manager> 

1.authentication-manager标签解析类为:org.springframework.security.config.authentication.AuthenticationManagerBeanDefinitionParser,具体解析方法parse的代码为:

public BeanDefinition parse(Element element, ParserContext pc) {
    String id = element.getAttribute("id");
    if (!StringUtils.hasText(id)) {
        // 判断是否有注册过BeanIds.AUTHENTICATION_MANAGER
        if (pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)) {
            // 如果BeanIds.AUTHENTICATION_MANAGER已经被注册 添加错误信息
            pc.getReaderContext().warning("Overriding globally registered AuthenticationManager", pc.extractSource(element));
        }
        // 配置id默认值为BeanIds.AUTHENTICATION_MANAGER
        id = BeanIds.AUTHENTICATION_MANAGER;
    }

    pc.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)));
    // 构建ProviderManager的BeanDefinition  
    BeanDefinitionBuilder providerManagerBldr = BeanDefinitionBuilder.rootBeanDefinition(ProviderManager.class);
    // 获取ATT_ALIAS属性值
    String alias = element.getAttribute(ATT_ALIAS);

    List<BeanMetadataElement> providers = new ManagedList<BeanMetadataElement>();
    NamespaceHandlerResolver resolver = pc.getReaderContext().getNamespaceHandlerResolver();

    // 获取authentication-manager的子节点
    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;
            // 判断是否配置了ATT_REF属性
            if (StringUtils.hasText(providerElt.getAttribute(ATT_REF))) {
                // 若配置了ATT_REF属性,则不允许配置其他属性否则添加错误信息
                if (providerElt.getAttributes().getLength() > 1) {
                    pc.getReaderContext().error("authentication-provider element cannot be used with other attributes " + "when using 'ref' attribute", pc.extractSource(element));
                }
                // 判断是否有子节点
                NodeList providerChildren = providerElt.getChildNodes();
                for (int j = 0; j < providerChildren.getLength(); j++) {
                    // 如果有子节点则添加错误信息
                    if (providerChildren.item(j) instanceof Element) {
                        pc.getReaderContext().error("authentication-provider element cannot have child elements when used " + "with 'ref' attribute", pc.extractSource(element));
                    }
                }
                providers.add(new RuntimeBeanReference(providerElt.getAttribute(ATT_REF)));
            }else 
                // 如果没有ATT_REF属性,则通过子标签的解析类完成标签解析 详情移步2
                BeanDefinition provider = resolver.resolve(providerElt.getNamespaceURI()).parse(providerElt, pc);
                Assert.notNull(provider, "Parser for " + providerElt.getNodeName() + " returned a null bean definition");
                String providerId = pc.getReaderContext().generateBeanName(provider);
                // 注册provider的BeanDefinition  
                pc.registerBeanComponent(new BeanComponentDefinition(provider, providerId));
                // 添加注册过的bean到provider集合中  
                providers.add(new RuntimeBeanReference(providerId));
            }
        }
    }

    // 如果providers为空 添加NullAuthenticationProvider
    if (providers.isEmpty()) {
        providers.add(new RootBeanDefinition(NullAuthenticationProvider.class));
    }

    // 为providerManagerBldr注入参数
    providerManagerBldr.addConstructorArgValue(providers);

    // 判断ATT_ERASE_CREDENTIALS属性是否为false
    if ("false".equals(element.getAttribute(ATT_ERASE_CREDENTIALS))) {
        providerManagerBldr.addPropertyValue("eraseCredentialsAfterAuthentication", false);
    }

    // 构造DefaultAuthenticationEventPublisher的BeanDefinition
    BeanDefinition publisher = new RootBeanDefinition(DefaultAuthenticationEventPublisher.class);
    String pubId = pc.getReaderContext().generateBeanName(publisher);
    pc.registerBeanComponent(new BeanComponentDefinition(publisher, pubId));
    providerManagerBldr.addPropertyReference("authenticationEventPublisher", pubId);

    // 注册ProviderManager的bean  
    pc.registerBeanComponent(new BeanComponentDefinition(providerManagerBldr.getBeanDefinition(), id));

    // 判断是否有别名,有则注册别名
    if (StringUtils.hasText(alias)) {
        pc.getRegistry().registerAlias(id, alias);
        pc.getReaderContext().fireAliasRegistered(id, 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的注册任务

2.子标签解析类为:org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser,具体解析方法parse的代码为:

public BeanDefinition parse(Element element, ParserContext pc) {
    // 首先构造DaoAuthenticationProvider的BeanDefinition
    RootBeanDefinition authProvider = new RootBeanDefinition(DaoAuthenticationProvider.class);
    authProvider.setSource(pc.extractSource(element));

    // 获取password-encoder子标签 
    Element passwordEncoderElt = DomUtils.getChildElementByTagName(element, Elements.PASSWORD_ENCODER);

    // 判断是否有password-encoder
    if (passwordEncoderElt != null) {
        PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc);
        authProvider.getPropertyValues().addPropertyValue("passwordEncoder", pep.getPasswordEncoder());

        // 判断是否有salt-source标签
        if (pep.getSaltSource() != null) {
            authProvider.getPropertyValues().addPropertyValue("saltSource", pep.getSaltSource());
        }
    }

    // 获取user-service标签
    Element userServiceElt = DomUtils.getChildElementByTagName(element, Elements.USER_SERVICE);

    // 获取jdbc-user-service标签
    if (userServiceElt == null) {
        userServiceElt = DomUtils.getChildElementByTagName(element, Elements.JDBC_USER_SERVICE);
    }

    // 获取ldap-user-service标签
    if (userServiceElt == null) {
        userServiceElt = DomUtils.getChildElementByTagName(element, Elements.LDAP_USER_SERVICE);
    }

    // 获取user-service-ref属性
    String ref = element.getAttribute(ATT_USER_DETAILS_REF);

    if (StringUtils.hasText(ref)) {
        if (userServiceElt != null) {
            // 如果配置了user-service-ref属性 且有子标签 则添加错误信息
            pc.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);
        }

        authProvider.getPropertyValues().add("userDetailsService", new RuntimeBeanReference(ref));
    }else {
        // 利用子标签创建UserDetailsService
        if (userServiceElt != null) {
            pc.getDelegate().parseCustomElement(userServiceElt, authProvider);
        } else {
            // 添加错误信息
            pc.getReaderContext().error("A user-service is required", element);
        }

        // 查看是否配置缓存属性
        String cacheRef = userServiceElt.getAttribute(AbstractUserDetailsServiceBeanDefinitionParser.CACHE_REF);

        if (StringUtils.hasText(cacheRef)) {
            authProvider.getPropertyValues().addPropertyValue("userCache", new RuntimeBeanReference(cacheRef));
        }
    }

    return authProvider;
}

3.替换配置
如果不使用Spring Security标签来配置Spring Security,适用Spring基础标签标签,如何配置?请参考authentication-manager标签解析

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/RobertoHuang/article/details/71512874
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞