背景
Spring Boot
的配置文件中经常会有包括数据库密码在内的一些敏感信息,为了保证服务的安全性,最好对这些敏感的属性值进行加密,而加密后的属性是没有办法直接用的,需要解密后再使用。
EnvironmentPostProcessor
EnvironmentPostProcessor
是 Spring Boot
中的一个接口,它允许你在 Spring Environment
配置加载之前对其进行修改。 它提供了一个机会来修改应用程序的配置,包括系统属性、环境属性和 application.properties
或 application.yml
文件中的属性。
接下来我们将使用 EnvironmentPostProcessor
来实现配置文件中的属性解密功能。
- 新建一个DecryptPropertySource 继承 MapPropertySource
java
// 这里只继承了MapPropertySource.
public class DecryptPropertySource extends MapPropertySource {
private final MapPropertySource decryptPropertySource;
/**
* constractor.
*
* @param name source name
* @param decryptPropertySource decryptPropertySource
*/
public DecryptPropertySource(String name, MapPropertySource decryptPropertySource) {
super(name, decryptPropertySource.getSource());
this.decryptPropertySource = decryptPropertySource;
}
/**
* get property value by property name.
*
* @param name property name
* @return property value
*/
public Object getProperty(String name) {
Object value = decryptPropertySource.getProperty(name);
if (value instanceof String) {
// 在这里对你的属性值进行解密、处理等等,并返回处理后的属性值。
return ...;
}
return value;
}
/**
* get DecryptResource.
*
* @return DecryptResource
*/
public PropertySource<Map<String, Object>> getDecryptResource() {
return this.decryptPropertySource;
}
}
- 实现
EnvironmentPostProcessor
接口
java
public class DecryptionPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource<?> source : propertySources) {
if (source instanceof MapPropertySource) {
MapPropertySource mapPropertySource = (MapPropertySource) source;
// 这里使用replace方法对source进行替换.
propertySources.replace(mapPropertySource.getName(),
new DecryptPropertySource(mapPropertySource.getName(), mapPropertySource));
}
}
}
}
- 在
resources
目录下创建META_INF
目录,并在目录下创建spring.factories
文件,在文件中加入以下内容,指定EnvironmentPostProcessor
为刚才创建的实现类的全路径:
ini
org.springframework.boot.env.EnvironmentPostProcessor=\
com.demo.processor.CustomDecryptionPostProcessor
通过以上操作,程序在获取属性时就会使用 DecryptPropertySource
中的 getProperty
方法,获取到经过处理之后的值。
为什么用 propertySources.replace
而不是用 propertySources.addFirst
方法呢?
网上很多文章都在推荐使用 addFirst
方法,没错,确实可以将你想修改的属性放在一个 Map
中,然后使用 addFirst
方法,实现对属性值的修改。 但是,这种方式会产生一些问题,因为你需要考虑 environment
中配置文件的加载顺序以及属性名重复的问题,否则,可能会产生属性值被覆盖的问题。 比如以下两种情况:
- 项目中同时有
application.yml
和application-dev.yml
文件,同时有a属性
项目实际上指定了 activeProfile
为 application-dev.yml
文件,需要修改的值是 application-dev.yml
的 a
属性,但是由于 application.yml
中也有 a
属性,那么经过addFirst
方法, 最终生效的属性值为 application.yml
中的 a
属性经过处理后的值。
- 项目自身有
application.yml
,项目引用的jar
包中有application.properties
,同时有a
属性
这种情况下,会导致生效的属性值为 application.properties
中 a
属性经过处理后的值,而非项目本身 application.yml
中的值。
这中间涉及到的各种配置文件的加载顺序,到时候会在下一篇文件中详细描述,大家有兴趣的也可以自己去研究一下。