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

一、说明

  • 该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的处理思路如下:

  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();
                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版权协议,转载请附上原文出处链接和本声明。