SpringBoot环境后置处理器EnvironmentPostProcessor(实现配置文件敏感信息加密)


前言

  • 看了MyBatisPlus的数据安全保护,对它的实现原理产生好奇,它是怎么做到的呢?后来又参考网上多篇优秀文章,作此笔记

一、介绍

  • SpringBoot的一个核心组件EnvironmentPostProcessor,叫做 环境后置处理器
  • 它是一个接口,我们可以实现它,它可以在创建应用程序上下文之前(项目启动之前),增、删、改 配置文件信息、环境变量、系统属性

二、应用场景

1、配置文件敏感信息解密

  • 配置文件中敏感信息写入的是加密的信息,程序启动时需要解密,框架才能使用

1)配置文件

# 使用加密后的密文,使用统一的加密前缀mpw:
username: mpw:e31oCntywsc1nUkjU+wiGg==

2)代码实现

/**
 * 解密配置文件
 * 参考MyBatisPlus的数据安全保护
 *
 * @author kimi
 * @date 2022/11/24
 */
public class ConfigDecryptProcessor implements EnvironmentPostProcessor{

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) {
        /**
         * 1、环境变量
         */
        Map<String, Object> systemEnvironment=environment.getSystemEnvironment();

        /**
         * 2、系统属性(=System.getProperties())
         */
        Map<String,Object> systemProperties=environment.getSystemProperties();

        /**
         * 3、配置文件
         */
        //单个属性
        Integer port=environment.getProperty("server.port",Integer.class);

        //激活的配置文件名称(=spring-profiles-active)
        String[] activeProfiles=environment.getActiveProfiles();
        //添加配置文件
        //environment.addActiveProfile();
        //设置配置文件
        //environment.setActiveProfiles();

        /**
         * 4、所有的配置源
         *    包含 环境变量、系统属性、配置文件 . . .
         */
        MutablePropertySources mutablePropertySources=environment.getPropertySources();

        /**
         * 1)获取密钥
         */
        String mpwKey=null;
        for (PropertySource<?> ps:mutablePropertySources) {
            /**
             * 配置源名称
             *  configurationProperties
             *  servletConfigInitParams
             *  servletContextInitParams
             *  systemProperties
             *  systemEnvironment
             *  random
             *  applicationConfig: [classpath:/config/application-druid.yml]
             *  applicationConfig: [classpath:/config/application.yml]
             */
            String psName=ps.getName();

            /**
             * 配置源PropertySource是一个抽象类,它的实现类有:
             *      OriginTrackedMapPropertySource:配置文件(application*.yml/properties)
             *      RandomValuePropertySource:random
             *      ServletConfigPropertySource servletConfigInitParams
             *      ServletContextPropertySource servletContextInitParams
             *      CommandLinePropertySource、SimpleCommandLinePropertySource:以输入命令行参数作为配置源:启动命令、idea的Program arguments
             *
             */
            //我们在启动命令中指定了密钥:--mpw.key=密钥,所以使用SimpleCommandLinePropertySource实现类
            if (ps instanceof SimpleCommandLinePropertySource) {
                SimpleCommandLinePropertySource source=(SimpleCommandLinePropertySource) ps;
                mpwKey=source.getProperty("mpw.key");
            }
        }
        //密钥为空则无需解密
        System.err.println("mpwKey: "+mpwKey);
        if(mpwKey==null || mpwKey.isEmpty()){
            return;
        }

        /**
         * 2)解密配置文件
         */
        //解密配置文件中加密的信息,放入一个Map
        Map<String,Object> map=new HashMap<>();
        for (PropertySource<?> ps:mutablePropertySources) {
            //加密信息在配置文件中,所以使用OriginTrackedMapPropertySource实现类
            if (ps instanceof OriginTrackedMapPropertySource) {
                OriginTrackedMapPropertySource source=(OriginTrackedMapPropertySource) ps;
                //遍历所有的配置信息
                for (String name:source.getPropertyNames()) {
                    Object value=source.getProperty(name);
                    if (value instanceof String) {
                        String str=(String) value;
                        //包含加密前缀,截取去掉统一的加密前缀后的信息
                        if(str.startsWith("mpw:")){
                            map.put(name,解密方法(str.substring(5)));
                        }
                    }
                }
            }
        }
        System.err.println("解密后的配置: "+map.toString());

        //解密的Map有值,则添加到配置信息最前面,这样才会覆盖后面加密的配置信息(也就是加密的配置不生效,解密的配置生效)
        if (!map.isEmpty()) {
            mutablePropertySources.addFirst(new MapPropertySource("custom-encrypt", map));
        }
    }
}

3)驱动

  • springboot启动的时候,不会去主动读取我们自定义的类的,所以我们还需要利用SpringBoot的SPI机制,来加载我们的环境后置处理器
  • 在resources下创建META-INF/spring.factories,指定实现类的全限定名称(多个逗号分隔)
    org.springframework.boot.env.EnvironmentPostProcessor=com.race.framework.init.ConfigDecryptProcessor
    
  • key就是EnvironmentPostProcessor类的全限定名
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kimi-001

只想在有限的时间分享更多的知识

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值