美文网首页
自定义BeanFactoryPostProcessor实现配置文

自定义BeanFactoryPostProcessor实现配置文

作者: simoscode | 来源:发表于2018-05-09 13:39 被阅读81次

    1背景

    在spring开发过程中,使用到配置文件中通常保护一些敏感信息,比如数据库密码,redis登陆密码等.这些敏感信息直接明文保存在配置文件中是不太安全的.因此我们希望在配置文件中把敏感信息加密,在运行时动态解密.

    2实现思路

    在具体实现之前,我们得有一个问题的解决思路.思考一下,我们通过@Value@ConfigurationProperties也一样)给一个bean注入属性值,这个过程怎么实现的?在容器初始化过程先加载配置文件把这些值是保存在PropertySources中,然后容器实例化这个bean后,从PropertySources查找@Value名称对应的值,并把这个值赋给这个属性.因此,我们希望在容器加载完配置文件后,bean注入属性之前,把加密的信息解密掉,这样就可以达到动态解密的目的.

    3具体实现

    在实现思路里头,讲到 容器加载完配置文件后,bean注入属性之前,把加密的信息解密掉. 这就需要去扩展容器的实现.在Spring中提供了BeanFactoryPostProcessor.这个接口的语义与BeanPostProcessor相似,BeanFactoryPostProcessor定义在ApplicationContext会被自动执行,以便运用于修改定义在容器中的元数据配置.解密的BeanFactoryPostProcessor实现如下:

    package com.simos.decrypt;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.BeanFactoryAware;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.core.Ordered;
    import org.springframework.core.env.ConfigurableEnvironment;
    import org.springframework.core.env.MapPropertySource;
    import org.springframework.core.env.MutablePropertySources;
    import org.springframework.core.env.PropertySource;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Created by l2h on 18-5-8.
     * Desc:BeanFactoryPostProcessor完成配置文件中属性解密
     * @author l2h
     */
    public class SimosBeanFactoryPostProcessor implements BeanFactoryPostProcessor,BeanFactoryAware,Ordered {
    
    /**
     * 应用环境,用于获取配置属性
     */
    private ConfigurableEnvironment environment;
    private BeanFactory beanFactory;
    /**
     * 完成解密的接口
     */
    private EncryptStringValueResolver valueResolver;
    public SimosBeanFactoryPostProcessor(ConfigurableEnvironment environment ){
        this.environment = environment;
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactoryToProcess) throws BeansException {
                /**
                 * 获取所有配置属性
                 */
                valueResolver = (EncryptStringValueResolver)beanFactory.getBean("encryptStringValueResolver");
                MutablePropertySources propertyValues =  environment.getPropertySources();
                decryptProperty(propertyValues,valueResolver);
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    
    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
    
    /**
     * 解密方法.遍历每个PropertySources,遍历PropertySources的每个键值如果包含加密信息就解密,最后替换.
     * @param propertyValues
     * @param valueResolver
     */
    private void decryptProperty(MutablePropertySources propertyValues,EncryptStringValueResolver valueResolver) {
        propertyValues.forEach(pv->{
            PropertySource propertySource = propertyValues.get(pv.getName());
            if (propertySource instanceof MapPropertySource){
                //保存处理过后的值
                Map<String,Object> convertPropertySource = new HashMap<>();
                //重新定义一个PropertySource,覆盖原来的
                MapPropertySource newMapPropertySource = new MapPropertySource(pv.getName(),convertPropertySource);
                String[] propertyNames = ((MapPropertySource) propertySource).getPropertyNames();
                for (int i=0,length = propertyNames.length;i<length;i++){
                   Object value = propertySource.getProperty(propertyNames[i]);
                   if (value instanceof String){
                       convertPropertySource.put(propertyNames[i], valueResolver.resolveEncryptStringValue((String) value));
                   }
                   else {
                       convertPropertySource.put(propertyNames[i],value);
                   }
                }
                //处理过后值替换原来的PropertySource
                propertyValues.replace(pv.getName(),newMapPropertySource);
            }
    
        });
    
    }
    }
    

    主要是通过ConfigurableEnvironment获取所有的配置属性,SimosStringValueResolver包含具体解密实现.
    其他业务代码实现,不详细讲解,代码均有说明.github传送门:样例源码

    小结

    这个功能主要是使用BeanFactoryPostProcessor接口,在Spring IoC容器初始化的过程添加一下自定义逻辑.

    相关文章

      网友评论

          本文标题:自定义BeanFactoryPostProcessor实现配置文

          本文链接:https://www.haomeiwen.com/subject/tqlnrftx.html