一、说明
- 该starter在被项目依赖后会在Springboot启动之初自动完成加密属性的解密并将解密后的内容替换到springboot配置容器中。
- 加密的属性在以固定的格式开头,例如:key = e n c r y p t i o n encryptionencryptionabcdefg
- 可以在配置文件中指定自定义的解密类的全限定名,调用格式如下:decrypt.class = com.zxl.decrypt.noOpDecrypt
- 使用方式:直接依赖启动即可
二、starter创建过程
1、在IDEA中建立一个空的工程,在工程中分别创建两个模块decrypt-springboot-starter和decrypt-springboot-autoconfiguration,decrypt-springboot-starter模块没有实际功能,并依赖于decrypt-springboot-autoconfiguration模块。
2、构建decrypt-springboot-autoconfiguration模块,该模块实现了starter的核心功能,为了构建stater,在该模块依赖springboot官方提供的自动配置jar包,注意为了防止依赖冲突,将jar包的作用域设置为私有。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.3.0.RELEASE</version>
<scope>provided</scope>
</dependency>
3、由于该starter的功能是在其它starter加载之前完成加密属性的替换,因此可以使用一个实现了环境后置处理器的类MyEnvironmentPostProcessor来实现,在springboot完成系统环境的加载之后会根据spring.factory文件的指引调用该类的postProcessEnvironment方法,步骤如下:
- 在META-INF目录下创建spring.factory文件,并在文件中注册EnvironmentPostProcessor:
org.springframework.boot.env.EnvironmentPostProcessor=\
com.taobao.starter.MyEnvironmentPostProcessor
- 创建MyEnvironmentPostProcessor类并实现EnvironmentPostProcessor接口

4、在MyEnvironmentPostProcessor中实现后置处理器方法,postProcessEnvironment的处理思路如下:
- 获得来自于applicationConfig的键值对属性资源
- 判断用户是否人为指定了解密实现类,如果有通过发射失利话解密类
- 如果根据用户的配置没有获取到解析类对象,则采用原始的解析类
- 遍历application文件中所有的键值对
- 检查键值对是否为加密数据,如果是的话解密并把解密后的键值对保存到map中
- 将map中所有的键值对组成一个属性源并加载到上下文环境,完成覆盖
程序如下:
public class MyEnvironmentPostProcessor implements EnvironmentPostProcessor {
private static final String APPLICATION_SOURCE_NAME = "applicationConfig: [classpath:/application.properties]";
private static final String SECRET = "$encryption$";
private static final String DECRYPTCLASS = "decrypt.class";
//解密类实例
private Decrypt decryptClass;
public void postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) {
//1、获得来自于applicationConfig的键值对属性资源
MapPropertySource mapPropertySource =
(MapPropertySource) environment.getPropertySources().get(APPLICATION_SOURCE_NAME);
if(mapPropertySource==null) return;
//2、判断用户是否人为指定了解密实现类,如果有通过发射失利话解密类
String decryptClassName = (String)mapPropertySource.getProperty(DECRYPTCLASS);
if(null!=decryptClassName){
Class<?> clazz = null;
try {
clazz = this.getClass().getClassLoader().loadClass(decryptClassName);
//如果该类不是接口并且实现了Decrypt的类,才实例化创建
if(!clazz.isInterface()||Decrypt.class.isAssignableFrom(clazz))
decryptClass = (Decrypt) clazz.newInstance();
else
System.out.println("自定义解码类失败");
} catch (Exception e) {
System.out.println("自定义解码类失败");
}
}
//3、如果根据用户的配置没有获取到解析类对象,则采用原始的解析类
if(decryptClass==null) decryptClass = new fixDecrypt();
//4、遍历application文件中所有的键值对
String[] propertyNames = mapPropertySource.getPropertyNames();
HashMap<String, Object> map = new HashMap(1);
for (String s : propertyNames) {
5、检查键值对是否为加密数据,如果是的话解密并把解密后的键值对保存到map中
String nVal = check((String)mapPropertySource.getProperty(s));
if(null!=nVal) {
map.put(s, decryptClass.decypt(nVal));
System.out.println(s+":" +SECRET+nVal+"==>"+decryptClass.decypt(nVal));
}
}
6、将map中所有的键值对组成一个属性源并加载到上下文环境,完成覆盖
PropertySource source = new MapPropertySource("decryptSource", map);
environment.getPropertySources().addFirst(source);
}
/**
* 判断value是否以SECRET开头
* @param value
* @return如果是,返回之后的字符串,如果不是,返回空
*/
private String check(String value){
if(value.length()<SECRET.length()) return null;
if(value.substring(0,SECRET.length()).equals(SECRET))
return value.substring(SECRET.length(),value.length());
return null;
}
}
三、使用方式
1、工程中依赖该starter
<dependency>
<groupId>com.taobao.zxl</groupId>
<artifactId>decrypt-springboot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2、直接启动即可,如果要自定义解析类,则实现Decrypt接口再指定全限定名即可
public class noOpDecrypt implements Decrypt {
@Override
public String decypt(String s) {
return s;
}
}
版权声明:本文为qq_38001359原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。