springboot 2.6.12 自定义解析 yaml 加密数据

文章目录

        • 一、简介
        • 二、yaml 默认解析简单说明
        • 三、自定义 yaml 解密解析器
        • 四、配置 PropertySourceLoader
        • 五、简单测试

一、简介

为了保证项目的配置文件的安全性,需要对第三方组件 mysql、redis、es、mq 等用户名密码进行加密处理,可以使用现成的三方包 jasypt-spring-boot-starter 实现,可参考我另一篇文章 https://blog.csdn.net/qq_41538097/article/details/127075430

由于项目需要使用国密算法, jasypt-spring-boot-starter 实现不太方便,下面简单介绍自定义实现解析 yaml 加密数据

springboot 版本 2.6.12

二、yaml 默认解析简单说明

简单介绍以下 springboot 内部 yaml 解析
springboot 2.6.12 自定义解析 yaml 加密数据_第1张图片
默认解析 yaml 是 OriginTrackedYamlLoader 类 的 load 方法,我们只要继承 YamlProcessor 重写 load 方法即可

class OriginTrackedYamlLoader extends YamlProcessor {
    List<Map<String, Object>> load() {
        List<Map<String, Object>> result = new ArrayList();
        this.process((properties, map) -> {
            result.add(this.getFlattenedMap(map));
        });
        return result;
    }
}    

pom.xml,只导入 spring-boot-starter-web 包

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

下面是简单实现

三、自定义 yaml 解密解析器

自定义 yaml 解密解析器 DecryptYamlPropertySourceLoader

package com.ye.config;

import org.springframework.beans.factory.config.YamlProcessor;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 对 SpringBoot 默认的 Yaml 解析器进行重写
 * 对加密的配置项进行解密操作
 */
public class DecryptYamlPropertySourceLoader implements PropertySourceLoader {

    public DecryptYamlPropertySourceLoader() {
    }

    @Override
    public String[] getFileExtensions() {
        return new String[]{"yml", "yaml"};
    }

    @Override
    public List<PropertySource<?>> load(String name, Resource resource) {
        if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", this.getClass().getClassLoader())) {
            throw new IllegalStateException("Attempted to load " + name + " but snakeyaml was not found on the classpath");
        } else {
            List<Map<String, Object>> loaded = (new DecryptYamlPropertySourceLoader.Processor(resource)).load();
            if (loaded.isEmpty()) {
                return Collections.emptyList();
            } else {
                List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());

                for (int i = 0; i < loaded.size(); ++i) {
                    String documentNumber = loaded.size() != 1 ? " (document #" + i + ")" : "";
                    propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, Collections.unmodifiableMap((Map) loaded.get(i)), true));
                }

                return propertySources;
            }
        }
    }


    private static class Processor extends YamlProcessor {
        Processor(Resource resource) {
            super();
            this.setResources(resource);
        }

        public List<Map<String, Object>> load() {
            List<Map<String, Object>> result = new ArrayList<>();
            this.process((properties, map) -> {
                // 关键点,遍历解析出来的 Map,对加密配置解密
                Map<String, Object> decryptMap = getDecryptFlattenedMap(map);
                result.add(Processor.this.getFlattenedMap(decryptMap));
            });
            return result;
        }

        private Map<String, Object> getDecryptFlattenedMap(Map<String, Object> source) {
            Map<String, Object> result = new LinkedHashMap<>();
            buildDecryptFlattenedMap(result, source, null);
            return result;
        }

        private void buildDecryptFlattenedMap(Map<String, Object> result, Map<String, Object> source, @Nullable String path) {
            source.forEach((key, value) -> {
                if (StringUtils.hasText(path)) {
                    if (key.startsWith("[")) {
                        key = path + key;
                    } else {
                        key = path + '.' + key;
                    }
                }
                if (value instanceof String) {
                    result.put(key, decrypt(value.toString()));
                } else if (value instanceof Map) {
                    // Need a compound key
                    @SuppressWarnings("unchecked")
                    Map<String, Object> map = (Map<String, Object>) value;
                    buildDecryptFlattenedMap(result, map, key);
                } else if (value instanceof Collection) {
                    // Need a compound key
                    @SuppressWarnings("unchecked")
                    Collection<Object> collection = (Collection<Object>) value;
                    if (collection.isEmpty()) {
                        result.put(key, "");
                    } else {
                        int count = 0;
                        for (Object object : collection) {
                            buildDecryptFlattenedMap(result, Collections.singletonMap(
                                    "[" + (count++) + "]", object), key);
                        }
                    }
                } else {
                    result.put(key, (value != null ? value : ""));
                }
            });
        }

        private String decrypt(String value) {
            // 解密配置格式为 Ö‿Ö(密文)
            if (StringUtils.hasText(value) && value.startsWith("Ö‿Ö(") && value.endsWith(")")) {
            	// 可以自定义你的解密算法
                return "已解密";
            }
            return value;
        }
    }

}

四、配置 PropertySourceLoader

配置 PropertySourceLoader,在 resource 目录下新建 /META-INF/spring.factories文件,添加如下内容

org.springframework.boot.env.PropertySourceLoader=com.ye.config.DecryptYamlPropertySourceLoader

在这里插入图片描述

五、简单测试

application.yaml 添加如下内容,测试 List 、Map 类型

test:
  yaml:
    resolve:
      map: { "userName": Ö‿Ö(admin),"sex": "男" }
      list: [ Ö‿Ö(aa), Ö‿Ö(bb),3]

使用接口测试

@RestController
@RequestMapping("/resolve")
@Data
@ConfigurationProperties("test.yaml.resolve")
public class TestYamlResolve {

    private Map<String,?> map;
    private List<String> list;

    @GetMapping("/map")
    public Map<String,?> map() {
        return map;
    }
    @GetMapping("/list")
    public List<String> list() {
        return list;
    }
}

启动项目,测试结果如下,可以看到 Map 类型 和 List 类型解密成功

http://127.0.0.1:10001/resolve/map
springboot 2.6.12 自定义解析 yaml 加密数据_第2张图片

http://127.0.0.1:10001/resolve/list
springboot 2.6.12 自定义解析 yaml 加密数据_第3张图片

有可能理解不到位,还需要重写其他方法,目前运行正常,后面遇到问题在追加

你可能感兴趣的:(springboot,spring,boot,java,spring)