把一些密码的明文放在配置文件中毕竟不妥。改为密文,然后在装载到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);@Overridepublic 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 临窗旋墨