使用版本:
SpringBoot:2.0.6
jasypt-spring-boot-starter:2.0.0
spring-cloud-starter-alibaba-nacos-config:2.0.1.RELEASE
问题现象:
首次通过Nacos配置中心获取配置,对于ENC(XXXX)能正常解密,后续修改配置中心配置,触发配置刷新后,无法正常解密,配置内容变为ENC(XXXX)
源码解析分析参考:
https://blog.csdn.net/u013905744/article/details/86508236
在对jasypt和nacos的源码进行分析后,做了一些不太优雅的改动。具体方式如下:
1、在src目录下新建包,路径为:com.alibaba.cloud.nacos.client
2、将NacosPropertySourceLocator.java的源码进行修改,并贴到上面的包路径下,修改后代码为:
/* * Copyright (C) 2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.cloud.nacos.client; import com.alibaba.cloud.nacos.NacosConfigProperties; 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.context.ConfigurableApplicationContext; 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.List; import static com.ulisesbocchio.jasyptspringboot.configuration.EncryptablePropertyResolverConfiguration.RESOLVER_BEAN_NAME; /** * @author xiaojing * @author pbting * @author liuyuxiang */ @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 static final String SHARED_CONFIG_SEPARATOR_CHAR = "[,]"; private NacosPropertySourceBuilder nacosPropertySourceBuilder; private NacosConfigProperties nacosConfigProperties; @Autowired private ConfigurableListableBeanFactory beanFactory; @Autowired private ConfigurableApplicationContext context; public NacosPropertySourceLocator(NacosConfigProperties nacosConfigProperties) { this.nacosConfigProperties = nacosConfigProperties; } @Override public PropertySource<?> locate(Environment env) { ConfigService configService = nacosConfigProperties.configServiceInstance(); if (null == configService) { log.warn("no instance of config service found, can't load config from nacos"); return null; } long timeout = nacosConfigProperties.getTimeout(); nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService, timeout); String name = nacosConfigProperties.getName(); String dataIdPrefix = nacosConfigProperties.getPrefix(); if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; } if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = env.getProperty("spring.application.name"); } CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); loadSharedConfiguration(composite); loadExtConfiguration(composite); loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); EncryptablePropertyResolver propertyResolver = beanFactory.getBean(RESOLVER_BEAN_NAME, EncryptablePropertyResolver.class); return EncryptablePropertySourceConverter.makeEncryptable(InterceptionMode.PROXY, propertyResolver, composite); } private void loadSharedConfiguration( CompositePropertySource compositePropertySource) { String sharedDataIds = nacosConfigProperties.getSharedDataids(); String refreshDataIds = nacosConfigProperties.getRefreshableDataids(); if (sharedDataIds == null || sharedDataIds.trim().length() == 0) { return; } String[] sharedDataIdArray = sharedDataIds.split(SHARED_CONFIG_SEPARATOR_CHAR); checkDataIdFileExtension(sharedDataIdArray); for (int i = 0; i < sharedDataIdArray.length; i++) { String dataId = sharedDataIdArray[i]; String fileExtension = dataId.substring(dataId.lastIndexOf(".") + 1); boolean isRefreshable = checkDataIdIsRefreshable(refreshDataIds, sharedDataIdArray[i]); loadNacosDataIfPresent(compositePropertySource, dataId, "DEFAULT_GROUP", fileExtension, isRefreshable); } } private void loadExtConfiguration(CompositePropertySource compositePropertySource) { List<NacosConfigProperties.Config> extConfigs = nacosConfigProperties .getExtConfig(); if (CollectionUtils.isEmpty(extConfigs)) { return; } checkExtConfiguration(extConfigs); for (NacosConfigProperties.Config config : extConfigs) { String dataId = config.getDataId(); String fileExtension = dataId.substring(dataId.lastIndexOf(DOT) + 1); loadNacosDataIfPresent(compositePropertySource, dataId, config.getGroup(), fileExtension, config.isRefresh()); } } private void checkExtConfiguration(List<NacosConfigProperties.Config> extConfigs) { String[] dataIds = new String[extConfigs.size()]; for (int i = 0; i < extConfigs.size(); i++) { String dataId = extConfigs.get(i).getDataId(); if (dataId == null || dataId.trim().length() == 0) { throw new IllegalStateException(String.format( "the [ spring.cloud.nacos.config.ext-config[%s] ] must give a dataId", i)); } dataIds[i] = dataId; } checkDataIdFileExtension(dataIds); } private void loadApplicationConfiguration( CompositePropertySource compositePropertySource, String dataIdPrefix, NacosConfigProperties properties, Environment environment) { String fileExtension = properties.getFileExtension(); String nacosGroup = properties.getGroup(); // load directly once by default loadNacosDataIfPresent(compositePropertySource, dataIdPrefix, nacosGroup, fileExtension, true); // load with suffix, which have a higher priority than the default loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true); // Loaded with profile, which have a higher priority than the suffix for (String profile : environment.getActiveProfiles()) { String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true); } } private void loadNacosDataIfPresent(final CompositePropertySource composite, final String dataId, final String group, String fileExtension, boolean isRefreshable) { if (null == dataId || dataId.trim().length() < 1) { return; } if (null == group || group.trim().length() < 1) { return; } NacosPropertySource propertySource = this.loadNacosPropertySource(dataId, group, fileExtension, isRefreshable); this.addFirstPropertySource(composite, propertySource, false); } private NacosPropertySource loadNacosPropertySource(final String dataId, final String group, String fileExtension, boolean isRefreshable) { if (NacosContextRefresher.getRefreshCount() != 0) { if (!isRefreshable) { return NacosPropertySourceRepository.getNacosPropertySource(dataId); } } return nacosPropertySourceBuilder.build(dataId, group, fileExtension, isRefreshable); } /** * Add the nacos configuration to the first place and maybe ignore the empty * configuration. */ private void addFirstPropertySource(final CompositePropertySource composite, NacosPropertySource nacosPropertySource, boolean ignoreEmpty) { if (null == nacosPropertySource || null == composite) { return; } if (ignoreEmpty && nacosPropertySource.getSource().isEmpty()) { return; } composite.addFirstPropertySource(nacosPropertySource); } private static void checkDataIdFileExtension(String[] dataIdArray) { if (dataIdArray == null || dataIdArray.length < 1) { throw new IllegalStateException("The dataId cannot be empty"); } // Just decide that the current dataId must have a suffix NacosDataParserHandler.getInstance().checkDataId(dataIdArray); } private boolean checkDataIdIsRefreshable(String refreshDataIds, String sharedDataId) { if (StringUtils.isEmpty(refreshDataIds)) { return false; } String[] refreshDataIdArray = refreshDataIds.split(SHARED_CONFIG_SEPARATOR_CHAR); for (String refreshDataId : refreshDataIdArray) { if (refreshDataId.equals(sharedDataId)) { return true; } } return false; } }
修改内容核心逻辑:加粗标红代码
原理:主要是结合jasypt的实现原理,对nacos生成的propertySource进行包装,包装成EncryptableXXX
小知识:自己写的同包路径下的同名类会覆盖jar包中的同包路径下的同名类,比如本文重写的这些代码,在程序运行时会覆盖原来nacos jar包中的代码而生效。
完毕。
为了方便演示,直接在源码上修改,后续如果升级nacos版本可能会带来问题,仅为抛砖引玉。