zoukankan      html  css  js  c++  java
  • 让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版本可能会带来问题,仅为抛砖引玉。

  • 相关阅读:
    Python入门11 —— 基本数据类型的操作
    Win10安装7 —— 系统的优化
    Win10安装6 —— 系统的激活
    Win10安装5 —— 系统安装步骤
    Win10安装4 —— 通过BIOS进入PE
    Win10安装2 —— 版本的选择与下载
    Win10安装1 —— 引言与目录
    Win10安装3 —— U盘启动工具安装
    虚拟机 —— VMware Workstation15安装教程
    Python入门10 —— for循环
  • 原文地址:https://www.cnblogs.com/flying607/p/12520009.html
Copyright © 2011-2022 走看看