把一些密码的明文放在配置文件中毕竟不妥。改为密文,然后在装载到spring的时候解密稍微安全一点。
实现spring加解密的方式有很多,比如jasypt-spring-boot-starter
就提供了这样的方式,不过本人觉得这个功能比较简单,不是特别想引入其他依赖,就随手写一个。
本质上和jasypt
一样都是对对ConfigurableEnvironment
中的配置进行处理,比如jasypt-spring-boot-starter
的处理时机是 EnableEncryptablePropertiesBeanFactoryPostProcessor implements BeanFactoryPostProcessor#postProcessBeanFactory
,也就是在bean实例化以后但是在调用其初始化方法后进行回调以达到对bean进行处理的效果。
(此处提醒使用前后置处理的时候可能会提升某些bean的初始化时机,需要注意)。
源码地址: 临窗旋墨/boot
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
的开头位置;
org.springframework.boot.env.EnvironmentPostProcessor=pers.vic.boot.base.tool.evn.DecryptEnvironmentPostProcessor
…
jasypt-spring-boot-starter
,省却自己写代码jasypt
使用#postProcessBeanFactory 后置处理器对spring环境进行处理PropertyPlaceholderConfigurer
,直接替换属性,原理都类似在写项目的时候要有一定的安全意识,注意对资源的保护。对于这些重要的资源和中间件(如zk,mysql,redis,nacos等等),建议不要对公网开放,若真的需要开发公网,则注意限定客户端ip,以及软件本身的权限限制(前往不要使用root权限)。
2022-03-22 随手记 by Vic.xu 临窗旋墨