Nacos配置中心支持加密配置项动态刷新

问题描述

通过jasypt对Nacos中的敏感配置项经行加密处理后,首次通过Nacos配置中心获取配置,可以正常解密ENC(XXXX),修改配置中心中的任意配置,触发配置刷新后,无法正常解密,配置内容变为ENC(XXXX)。
注:在实现功能的时候,查阅了很多资料,最终参考了https://www.cnblogs.com/flying607/p/12520009.html中的方法,但是在实际应用过程中,按照他的方法并未达到预期的效果,可能是版本原因....吧,未深究,所以自己debug一遍,在合适的位子进行配置文件解密,最终达到自己期待的效果。

解决办法

重写NacosPropertySourceLocator类方法,在加载配置项的过程中经行解密,具体操作:
1、新建一个package,命名为:com.alibaba.cloud.nacos.client,一定不能错,否者无法覆盖原NacosPropertySourceLocator
2、新建一个NacosPropertySourceLocator class类,粘贴一下内容即可。


使用到的一些主要依赖,如下:
SpringBoot:2.1.18.RELEASE
jasypt-spring-boot-starter:3.0.2
jasypt-spring-boot:1.18
spring-cloud-starter-alibaba-nacos-config:2.1.2.RELEASE
如果遇到问题,可以先排除一下版本问题,有的nacos版本中没有NacosPropertySourceLocator 这个类,如果真是,那就自己再想想办法吧…


相关依赖配置如下

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot</artifactId>
    <version>1.18</version>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

在这里插入图片描述
NacosPropertySourceLocator 类

package com.alibaba.cloud.nacos.client;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.cloud.nacos.NacosConfigProperties.Config;
import com.alibaba.cloud.nacos.NacosPropertySourceRepository;
import com.alibaba.cloud.nacos.parser.NacosDataParserHandler;
import com.alibaba.cloud.nacos.refresh.NacosContextRefresher;
import com.alibaba.nacos.api.config.ConfigService;
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertyResolver;
import com.ulisesbocchio.jasyptspringboot.EncryptablePropertySourceConverter;
import com.ulisesbocchio.jasyptspringboot.InterceptionMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration.RESOLVER_BEAN_NAME;


/**
 * @author Littlexu
 * @Description 【覆盖第三方包里的文件,后期版本升级可能会有影响】
 *              解决nacos配置中心敏感信息加密问题:第二次修改配置文件后,ENC(****)无法解密,需要重启服务
 * @date 2022/5/31
 */
@Order(0)
public class NacosPropertySourceLocator implements PropertySourceLocator {
    private static final Logger log = LoggerFactory.getLogger(NacosPropertySourceLocator.class);
    private static final String NACOS_PROPERTY_SOURCE_NAME = "NACOS";
    private static final String SEP1 = "-";
    private static final String DOT = ".";
    private NacosPropertySourceBuilder nacosPropertySourceBuilder;
    private NacosConfigProperties nacosConfigProperties;
    private NacosConfigManager nacosConfigManager;

    @Autowired
    private ConfigurableListableBeanFactory beanFactory;

    public NacosPropertySourceLocator(NacosConfigManager nacosConfigManager) {
        this.nacosConfigManager = nacosConfigManager;
        this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
    }

    @Override
    public PropertySource<?> locate(Environment env) {
        this.nacosConfigProperties.setEnvironment(env);
        ConfigService configService = this.nacosConfigManager.getConfigService();
        if (null == configService) {
            log.warn("no instance of config service found, can't load config from nacos");
            return null;
        } else {
            long timeout = (long) this.nacosConfigProperties.getTimeout();
            this.nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout);
            String name = this.nacosConfigProperties.getName();
            String dataIdPrefix = this.nacosConfigProperties.getPrefix();
            if (StringUtils.isEmpty(dataIdPrefix)) {
                dataIdPrefix = name;
            }

            if (StringUtils.isEmpty(dataIdPrefix)) {
                dataIdPrefix = env.getProperty("spring.application.name");
            }

            CompositePropertySource composite = new CompositePropertySource("NACOS");
            this.loadSharedConfiguration(composite);
            this.loadExtConfiguration(composite);
            this.loadApplicationConfiguration(composite, dataIdPrefix, this.nacosConfigProperties, env);
            return composite;
        }
    }

    private void loadSharedConfiguration(CompositePropertySource compositePropertySource) {
        List<Config> sharedConfigs = this.nacosConfigProperties.getSharedConfigs();
        if (!CollectionUtils.isEmpty(sharedConfigs)) {
            this.checkConfiguration(sharedConfigs, "shared-configs");
            this.loadNacosConfiguration(compositePropertySource, sharedConfigs);
        }

    }

    private void loadExtConfiguration(CompositePropertySource compositePropertySource) {
        List<Config> extConfigs = this.nacosConfigProperties.getExtensionConfigs();
        if (!CollectionUtils.isEmpty(extConfigs)) {
            this.checkConfiguration(extConfigs, "extension-configs");
            this.loadNacosConfiguration(compositePropertySource, extConfigs);
        }

    }

    private void loadApplicationConfiguration(CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) {
        String fileExtension = properties.getFileExtension();
        String nacosGroup = properties.getGroup();
        this.loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true);
        this.loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + "." + fileExtension, nacosGroup, fileExtension, true);
        String[] var7 = environment.getActiveProfiles();
        int var8 = var7.length;

        for (int var9 = 0; var9 < var8; ++var9) {
            String profile = var7[var9];
            String dataId = dataIdPrefix + "-" + profile + "." + fileExtension;
            this.loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true);
        }

    }

    private void loadNacosConfiguration(final CompositePropertySource composite, List<Config> configs) {
        Iterator var3 = configs.iterator();

        while (var3.hasNext()) {
            Config config = (Config) var3.next();
            String dataId = config.getDataId();
            String fileExtension = dataId.substring(dataId.lastIndexOf(".") + 1);
            this.loadNacosDataIfPresent(composite, dataId, config.getGroup(), fileExtension, config.isRefresh());
        }

    }

    private void checkConfiguration(List<Config> configs, String tips) {
        String[] dataIds = new String[configs.size()];

        for (int i = 0; i < configs.size(); ++i) {
            String dataId = ((Config) configs.get(i)).getDataId();
            if (dataId == null || dataId.trim().length() == 0) {
                throw new IllegalStateException(String.format("the [ spring.cloud.nacos.config.%s[%s] ] must give a dataId", tips, i));
            }

            dataIds[i] = dataId;
        }

        NacosDataParserHandler.getInstance().checkDataId(dataIds);
    }

    private void loadNacosDataIfPresent(final CompositePropertySource composite, final String dataId, final String group, String fileExtension, boolean isRefreshable) {
        if (null != dataId && dataId.trim().length() >= 1) {
            if (null != group && group.trim().length() >= 1) {
                NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group, fileExtension, isRefreshable);
                // 对配置文件的加密内容进行解密
                EncryptablePropertyResolver propertyResolver = beanFactory.getBean(RESOLVER_BEAN_NAME, EncryptablePropertyResolver.class);
                propertySource = (NacosPropertySource) EncryptablePropertySourceConverter.makeEncryptable(InterceptionMode.PROXY, propertyResolver, propertySource);

                this.addFirstPropertySource(composite, propertySource, false);
            }
        }
    }

    private NacosPropertySource loadNacosPropertySource(final String dataId, final String group, String fileExtension, boolean isRefreshable) {
        return NacosContextRefresher.getRefreshCount() != 0L && !isRefreshable ? NacosPropertySourceRepository.getNacosPropertySource(dataId, group) : this.nacosPropertySourceBuilder.build(dataId, group, fileExtension, isRefreshable);
    }

    private void addFirstPropertySource(final CompositePropertySource composite, NacosPropertySource nacosPropertySource, boolean ignoreEmpty) {
        if (null != nacosPropertySource && null != composite) {
            if (!ignoreEmpty || !((Map) nacosPropertySource.getSource()).isEmpty()) {
                composite.addFirstPropertySource(nacosPropertySource);
            }
        }
    }

    public void setNacosConfigManager(NacosConfigManager nacosConfigManager) {
        this.nacosConfigManager = nacosConfigManager;
    }
}

说明:jasypt-spring-boot-starter:3.0.2主要的作用在我看来,就改变了bean的加载顺序吧,在实际应用的时候,项目启动时会执行NacosPropertySourceLocator里的public PropertySource<?> locate(Environment env){···}方法,如果不引入这个jar包,会导致lazyEncryptablePropertyResolver的bean对象无法获取。

***************************
APPLICATION FAILED TO START
***************************

Description:

A component required a bean named 'lazyEncryptablePropertyResolver' that could not be found.


Action:

Consider defining a bean named 'lazyEncryptablePropertyResolver' in your configuration.

测试

测试代码如下:
在这里插入图片描述

  • 第一次启动,nacos可以正常解密
    在这里插入图片描述
    在这里插入图片描述
  • 修改某个配置后,配置刷新后,解密失败
    在这里插入图片描述
    在这里插入图片描述

使用提供的解决方案:

  • 第一次启动:
    在这里插入图片描述
    在这里插入图片描述
  • 修改配置后
    在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值