springboot 配置加密
springboot 配置加密
前言
在一些国家项目中经常会要求不能暴露数据库链接和密码, 所以需要对配置文件里面的一些配置进行加密处理。
解决方法有两种:一种是在配置加载后还没给对应的配置类赋值的时候将密文解密成明文替换。第二种是配置类已经赋值好了在使用之前解密替换。
如果只是配置文件加密解密,我觉得第二种方式更好也更合适。
一、在配置类赋值之前解密
因为 springboot 读取到的配置最后都会存在于
Environment
对象中, 所以我们可以实现一个BeanFactoryPostProcessor
对Environment
处理;需要注意的是Environment
中的各种PropertySource
都是不可修改的,但是 springboot 在读取的时候应该是顺序读取(博主也没有看过源码,这个是看到根据测试结果猜出来的),所以我们可以在Environment
中第一个位置加入一个未加密的PropertySource
。
这种方式甚至可以修改配置文件中启动的端口号。
/**
* 方式一: 通过覆盖 Environment 配置
*/
@Configuration
public class EnvironmentBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
/**
* 所有的从配置文件中读取到的配置都存放在 Environment 中, 使用 OriginTrackedMapPropertySource 将配置存储起来
* {@link org.springframework.boot.env.YamlPropertySourceLoader#load}
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
StandardServletEnvironment environment = beanFactory.getBean(StandardServletEnvironment.class);
String[] keySet = new String[] {
"spring.datasource.druid.master.url",
"spring.datasource.druid.master.username",
"spring.datasource.druid.master.password",
"spring.datasource.druid.slave.url",
"spring.datasource.druid.slave.username",
"spring.datasource.druid.slave.password",
};
Iterator<PropertySource<?>> iterator = environment.getPropertySources().iterator();
List<PropertySource<?>> replacePropertyList = new ArrayList<>(1);
while (iterator.hasNext()) {
Map<String, Object> map = new HashMap<>();
PropertySource<?> propertySource = iterator.next();
if (propertySource instanceof OriginTrackedMapPropertySource) {
OriginTrackedMapPropertySource originSource = (OriginTrackedMapPropertySource) propertySource;
Map<String, Object> unModifiableMap = originSource.getSource();
Map<String, Object> any = MapUtil.getAny(unModifiableMap, keySet);
if (any.isEmpty()) {
continue;
}
unModifiableMap.forEach((r, s) -> {
if (any.containsKey(r)) {
s = EncodeUtil.sm2Decode(s.toString(), KeyType.PrivateKey);
}
map.put(r, s);
});
OriginTrackedMapPropertySource modifiablePropertySource = new OriginTrackedMapPropertySource(originSource.getName(), map);
replacePropertyList.add(modifiablePropertySource);
}
replacePropertyList.forEach(r -> {
environment.getPropertySources().addFirst(r);
});
}
}
}
二、修改赋值后加密的配置类
这个就很简单了, 配置类也是一个 bean, 是 bean 就会走 Spring 的生命周期。可以实现
BeanPostProcessor
对 bean 的值进行处理。
/**
* 方式二: 在对 DataSource 赋值之后再次处理
*/
@Configuration
public class MyConfig implements BeanPostProcessor, PriorityOrdered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DruidDataSource) {
DruidDataSource dataSource = (DruidDataSource) bean;
dataSource.setUrl(EncodeUtil.sm2Decode(dataSource.getUrl(), KeyType.PrivateKey));
dataSource.setUsername(EncodeUtil.sm2Decode(dataSource.getUsername(), KeyType.PrivateKey));
dataSource.setPassword(EncodeUtil.sm2Decode(dataSource.getPassword(), KeyType.PrivateKey));
}
return bean;
}
/**
* 必须在 ${@link ConfigurationPropertiesBindingPostProcessor} 之后执行
*/
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}