springboot 配置加密
- @[TOC](springboot 配置加密)
- 前言
- 一、在配置類賦值之前解密
- 二、修改賦值后加密的配置類
springboot 配置加密
- @[TOC](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}*/@Overridepublic 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 {@Overridepublic 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} 之后執行*/@Overridepublic int getOrder() {return Integer.MAX_VALUE;}
}