springboot通过EnvironmentPostProcessor对配置文件解密

原创 临窗旋墨 2022-03-22 阅读:6654 分类: java后端 专题: SpringBoot进阶实践 标签: springboot

springboot通过EnvironmentPostProcessor对配置文件解密

把一些密码的明文放在配置文件中毕竟不妥。改为密文,然后在装载到spring的时候解密稍微安全一点。

实现spring加解密的方式有很多,比如jasypt-spring-boot-starter就提供了这样的方式,不过本人觉得这个功能比较简单,不是特别想引入其他依赖,就随手写一个。

本质上和jasypt一样都是对对ConfigurableEnvironment中的配置进行处理,比如jasypt-spring-boot-starter的处理时机是 EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor#postProcessBeanFactory,也就是在bean实例化以后但是在调用其初始化方法进行回调以达到对bean进行处理的效果。

(此处提醒使用前后置处理的时候可能会提升某些bean的初始化时机,需要注意)。

源码在此

源码地址: 临窗旋墨/boot

java 复制代码
package pers.vic.boot.base.tool.evn;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
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 pers.vic.boot.util.ApplicationPropertiesUtil;
import pers.vic.boot.util.encrypt.Base62Enhance;

import java.util.HashMap;
import java.util.Map;

/**
 * 描述:配置文件解密后置处理器
 * 原理:spring application context refreshed之前定制application运行环境,插入/修改配置信息 <br />
 * 另:jasypt-spring-boot-starter的处理时机是 EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor#postProcessBeanFactory<br />
 * @author Vic.xu
 * @date 2022-03-22 9:31
 */
public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor {

    /**
     * 配置文件中配置salt的key
     */
    private static String salt_key = "decrypt.salt";

    /**
     * 获取配置文件的salt,没有则默认为vic.xu
     */
    private static String salt = ApplicationPropertiesUtil.getString(salt_key, "vic.xu");

    /**
     * 加密的属性的前缀:dec() + 密文    eg: dec()passWord
     */
    private final static String DECRYPT_PREFIX = "dec()";

    private final static int DECRYPT_PREFIX_LENGTH = DECRYPT_PREFIX.length();

    /**
     *创建一个简单的加密实例
     */
    private static Base62Enhance enhance = Base62Enhance.createInstance(salt);

    /**
     * 替换过的resource的name
     */
    private static final String DECRYPT_RESOURCE_NAME = "decryptResource";


    private Logger logger = LoggerFactory.getLogger(DecryptEnvironmentPostProcessor.class);

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();
        Map<String, Object> replacedMap = new HashMap<>();
        for (PropertySource<?> propertySource : propertySources) {
            if (propertySource instanceof MapPropertySource) {
                MapPropertySource ps = (MapPropertySource) propertySource;
                replace(ps, replacedMap);
            }
        }
        if (!replacedMap.isEmpty()) {
            logger.info("{} decrypt properties  has replace !", replacedMap.size());
            propertySources.addFirst(new MapPropertySource(DECRYPT_RESOURCE_NAME, replacedMap));
        }

    }

    /**
     * 找出需要解密的value并解密
     * @param ps
     * @param replacedMap
     */
    private void replace(MapPropertySource ps, Map<String, Object> replacedMap) {
        for (String name : ps.getPropertyNames()) {
            Object value = ps.getProperty(name);
            if (value != null) {
                String v = String.valueOf(value);
                //解密的value以dec()开头
                if (v.length() <= DECRYPT_PREFIX_LENGTH || !v.startsWith(DECRYPT_PREFIX)) {
                    continue;
                }
                v = v.substring(DECRYPT_PREFIX_LENGTH);
                v = enhance.decode(v);
                replacedMap.put(name, v);
            }
        }
    }

    /**
     * 对外提供的加密方法, 和此中解码方法对应
     */
    public static String enc(String value) {
        return enhance.encode(value);
    }
}

如上,利用EnvironmentPostProcessor的后置环境处理器,获取到全部的MapPropertySource类型的资源,根据值中是否以指定字符开始,作为依据进行解密,然后放到新建的MapPropertySource中,进而加入MutablePropertySources的开头位置;

使用:META-INF/spring.factories中配置:

properties 复制代码
org.springframework.boot.env.EnvironmentPostProcessor=pers.vic.boot.base.tool.evn.DecryptEnvironmentPostProcessor

测试略

...

其他:

  1. 可以考虑使用jasypt-spring-boot-starter,省却自己写代码
  2. 也可以使如jasypt使用#postProcessBeanFactory 后置处理器对spring环境进行处理
  3. 也可以自定义PropertyPlaceholderConfigurer,直接替换属性,原理都类似

结语

在写项目的时候要有一定的安全意识,注意对资源的保护。对于这些重要的资源和中间件(如zk,mysql,redis,nacos等等),建议不要对公网开放,若真的需要开发公网,则注意限定客户端ip,以及软件本身的权限限制(前往不要使用root权限)。

2022-03-22 随手记 by Vic.xu 临窗旋墨

评论区

avatar
未登录

暂无评论,来发表第一条评论吧~