当前位置: 代码迷 >> 综合 >> 如何实现一个精简版的key-center-starter
  详细解决方案

如何实现一个精简版的key-center-starter

热度:97   发布时间:2023-12-03 09:36:27.0

一、说明

  • 该starter在被项目依赖后会在Springboot启动之初自动完成加密属性的解密并将解密后的内容替换到springboot配置容器中。
  • 加密的属性在以固定的格式开头,例如:key = encryptionencryptionencryptionabcdefg
  • 可以在配置文件中指定自定义的解密类的全限定名,调用格式如下: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的处理思路如下:

  1. 获得来自于applicationConfig的键值对属性资源
  2. 判断用户是否人为指定了解密实现类,如果有通过发射失利话解密类
  3. 如果根据用户的配置没有获取到解析类对象,则采用原始的解析类
  4. 遍历application文件中所有的键值对
  5. 检查键值对是否为加密数据,如果是的话解密并把解密后的键值对保存到map中
  6. 将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();elseSystem.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 {
    @Overridepublic String decypt(String s) {
    return s;}
}
  相关解决方案