解决springboot启动jar包时加载了jar包内的配置文件

由于生产环境扫描出有开源组件jar包漏洞,于是进行了springboot升级。
升级后打包部署到生产环境,发现数据库连接报错密码错误。初步定位问题是加载的配置文件是jar包内部而非jar包同级目录的config/application.yml文件。

版本升级
组件升级前版本升级后版本
springcloudHoxton.SR92021.0.3
springboot2.4.32.6.8
排查过程
  1. 首先怀疑是生产环境配置出了文件,检查配置文件和服务器环境,没发现有错误。
  2. 接下来在测试环境将配置文件放在config/application.yml,部署此jar包,试图重现此问题,结果并不存在此问题,这就比较奇怪,令人费解。
  3. 怀疑是springboot设计导致配置文件加载顺序或者配置项读取方式有变。查询了相关资料,spring配置文件的加载顺序并没有变化。

针对jar包内外来说配置文件加载的先后顺序为:

  • jar包外的application.yml
  • jar包内的application.yml
  • jar包外的application-prod.yml

对于读取顺序来讲为:

  • 优先读取jar包本身同级目录下的config/application.yml;
  • 再读取jar包本身同级目录下的application.yml;
  • classpath根目录下的config目录下的application.yml;
  • classpath根目录下的application.yml。

如果同时存在.properties和.yml配置文件,会优先加载yml配置。

  1. 于是考虑配置项读取方式,询问项目成员得知他的确修改了一处关于数据库密码解密的代码。由于公司使用的是自己的一套算法对数据库密码进行加密,加密后作为配置项,因此需要对配置的密文进行解密。

之前获取配置的方式为:

PropertySource<?> applicationConfig = propertySources.get("applicationConfig");

升级后通过上述代码无法获取到配置,因此改为:

ConfigurableEnvironment environment = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
Iterator<PropertySource<?>> iterator = propertySources.iterator();

通过阅读代码(看到其中的两层for循环就有种预感),发现的确是代码层面有问题,代码如下:

ConfigurableEnvironment environment = ((ApplicationEnvironmentPreparedEvent) event).getEnvironment();
MutablePropertySources propertySources = environment.getPropertySources();
for (PropertySource<?> propertySource : propertySources) {
    boolean applicationConfig = propertySource.getName().contains("application");
    if (!applicationConfig) {
        continue;
    }
    Object source = propertySource.getSource();
    Map<String, Object> confMap = (Map) source;
    for (Map.Entry<String, Object> entry : confMap.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        ...
        // 解密为result
        System.setProperty(key, result);
    }
}

从代码看,getPropertySources拿到了所有配置文件,然后在解密配置项时是先从Map中拿到配置,然后解密后将值重新设置到配置项。由于propertySource先执行了jar包外部的配置,然后执行了jar包里面的配置,同一个key的值会被后执行的一次覆盖掉,而MutablePropertySources继承自迭代器Iterable,按说jar包里的配置每次都会覆盖jar包外的。

解决办法

找到了原因,修改就简单了。将外层for去除掉,通过ConfigurableEnvironment.getProperty()直接获取配置项的值,springboot会确保每次都是从jar包外部config/application.yml文件读取配置,存在多个需解密的配置项就在类中新建数组存放配置名称。

String value = environment.getProperty("key");
ConfigurableEnvironment类

从上述可以看到,问题的关键在于如何去干预配置项的读取和设置。于是我们简单看下ConfigurableEnvironment 类的源码。

public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
    //设置活动的配置文件
	void setActiveProfiles(String... profiles);
    // 增加活动的配置文件
	void addActiveProfile(String profile);
    // 设置默认的配置文件
	void setDefaultProfiles(String... profiles);
    // 获取PropertySource键值组合的集合
	MutablePropertySources getPropertySources();
    // 系统环境变量
	Map<String, Object> getSystemEnvironment();
    // 系统配置
	Map<String, Object> getSystemProperties();
    // 合并
	void merge(ConfigurableEnvironment parent);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值