• 让Nacos支持加密配置项的配置刷新


    使用版本:

    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版本可能会带来问题,仅为抛砖引玉。

  • 相关阅读:
    C#后台调用Delphi 的Ocx
    打包.NET程序
    Tomcat配置虚拟目录、多域名、多个Http监听端口的方式
    检测多个Jar包冲突的class
    SessionFactoryImpl.get错误:java.lang.ArrayIndexOutOfBoundsException: 68问题
    搭建内网搜索平台
    cvs update报错
    混淆和加密.NET开发工具
    CVS报错:could not find desired version问题的解决
    记录一次接口压力测试结果
  • 原文地址:https://www.cnblogs.com/flying607/p/12520009.html
Copyright © 2020-2023  润新知