问题描述
通过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可以正常解密
- 修改某个配置后,配置刷新后,解密失败
使用提供的解决方案:
- 第一次启动:
- 修改配置后