2021年4月1日(本內容非愚人節惡搞)新增:
jasypt在新版本3.0.3.Release中已經解決了這個問題,請大家直接用新版就行。

————————————————————————————————————————————————以下為原文——————————————————————————————————————————————————————————————————
使用版本:
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版本可能會帶來問題,僅為拋磚引玉。
