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

 2019-10-17 22:00  阅读(1555)
文章分类:Spring boot

最近在学习安全框架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标签解析


来源:[]()

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring Security4.0.3源码分析之authentication-manager标签解析

相关推荐