(超级干货)巧用SpringBoot扩展点EnvironmentPostProcessor实现属性解密功能

背景

Spring Boot 的配置文件中经常会有包括数据库密码在内的一些敏感信息,为了保证服务的安全性,最好对这些敏感的属性值进行加密,而加密后的属性是没有办法直接用的,需要解密后再使用。

EnvironmentPostProcessor

EnvironmentPostProcessorSpring Boot 中的一个接口,它允许你在 Spring Environment 配置加载之前对其进行修改。 它提供了一个机会来修改应用程序的配置,包括系统属性、环境属性和 application.propertiesapplication.yml 文件中的属性。

接下来我们将使用 EnvironmentPostProcessor 来实现配置文件中的属性解密功能。

  1. 新建一个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;
    }
}
  1. 实现 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));
            }
        }

    }
}
  1. 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.ymlapplication-dev.yml 文件,同时有a属性

项目实际上指定了 activeProfileapplication-dev.yml 文件,需要修改的值是 application-dev.ymla 属性,但是由于 application.yml 中也有 a 属性,那么经过addFirst 方法, 最终生效的属性值为 application.yml 中的 a 属性经过处理后的值。

  • 项目自身有 application.yml ,项目引用的 jar 包中有 application.properties,同时有 a 属性

这种情况下,会导致生效的属性值为 application.propertiesa 属性经过处理后的值,而非项目本身 application.yml 中的值。

这中间涉及到的各种配置文件的加载顺序,到时候会在下一篇文件中详细描述,大家有兴趣的也可以自己去研究一下。

相关推荐
kinlon.liu几秒前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
王哲晓22 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888826 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存27 分钟前
源码分析:LinkedList
java·开发语言
执键行天涯28 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_199342 分钟前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen42 分钟前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
Reese_Cool1 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言