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

news2025/2/25 9:30:41

文章目录

        • 一、简介
        • 二、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 解析
在这里插入图片描述
默认解析 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.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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
在这里插入图片描述

http://127.0.0.1:10001/resolve/list
在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/618833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【springboot项目开发】文件上传与下载

目录 总体介绍 文件上传 介绍 文件上传的前端需求 文件上传的前端代码 文件上传的后端需求 文件上传的后端代码 文件下载 介绍 前端需求 前端代码 后端需求 后端代码 总体介绍 文件的上传和下载功能&#xff0c;是项目开发过程中比较常见的业务需求&#xff0c;我们…

开源 Golang 微服务入门二:RPC 框架 Kitex

前言 前一篇笔记介绍了字节跳动的开源 Golang 微服务 HTTP 框架 Hertz&#xff0c; 如下&#xff1a; 开源 Golang 微服务入门一&#xff1a; HTTP 框架 Hertz 本文将要介绍同样是字节跳动开源的 Golang 微服务 RPC 框架 Kitex。 Kitex 简介 Kitex 字节跳动内部的 Golang 微…

nacos高级

一、什么是配置中心 在微服务架构中&#xff0c;当系统从一个单体应用被拆分成分布式系统上的一个个服务节点后&#xff0c;配置文件也必须跟着迁移&#xff08;分割&#xff09;&#xff0c;这样配置就分散了。不仅配置会分散&#xff0c;分散中还会包含着冗余。 配置中心将…

Linux进程、用户、权限命令

进程管理命令 进程和程序的区别 1 程序是静态概念&#xff0c;本身作为一种软件资源长期保存&#xff1b;而进程是程序的执行过程&#xff0c;它是动态概念&#xff0c;有一定的生命期&#xff0c;是动态产生和消亡的。 2 程序和进程无一一对应关系。一个进程在活动中可有顺序…

顺序表和链表的比较

本文主要内容&#xff1a;从存取方式、逻辑/物理结构、查找/插入/删除操作和空间分配的角度比较顺序表和链表&#xff0c;并从存储、运算、环境的角度对比应如何选取存储结构。 目录 一、顺序表和链表的比较1、存取&#xff08;读写&#xff09;方式2、逻辑/物理结构3、查找、…

AI人工智能领域精美绘图模板分享

1 人工智能的发展历程 如今人工智能的应用渗透了我们生活的方方面面&#xff0c;我们都知道人工智能的前景十分光明&#xff0c;在未来对于推进人类发展进程也是非常重要的&#xff0c;但其实人工智能的发展道路是极其曲折的&#xff0c;下面就将人工智能的发展历程分为如下六…

arcgis for javascript TileLayer 自定义高德地图图层

效果如图&#xff1a; 一、创建自定义切片层 要创建自定义图块层&#xff0c;您必须调用BaseTileLayer类的createSubclass()方法。命名自定义层为TintLayer 由于这一层需要知道在哪里访问预定义的图块&#xff0c;我们将创建一个属性。应用程序将为图层提供值&#xff0c;图…

全球首发 《NGINX 完全指南》中文版

原文作者&#xff1a;Derek DeJonghe of F5 原文链接&#xff1a;全球首发 | 《NGINX 完全指南》中文版 转载来源&#xff1a;NGINX 开源社区 NGINX唯一中文官方社区 &#xff0c;尽在nginx.org.cn 在社区小伙伴们的催促下&#xff0c;我们很高兴地与大家分享这个好消息&#…

关于阵列发射端的波束形成(相控阵)研究与仿真实践

说明 相控阵是一个很大的话题&#xff0c;相控阵说得直白一点就是通过控制天线阵列中各个天线的相位来使得波束指向我们想要的方向。现阶段相控阵雷达用得更多的还是军事领域&#xff0c;不过随着技术的进步、成本的下降以及小型化&#xff0c;相控阵雷达也逐渐被用于民用领域了…

Python之Gradio简单使用

目录 安装Gradio示例用法应用界面1. gr.Interface2. gr.Blocks Gradio的输入和输出组件输入组件&#xff08;Inputs&#xff09;输出组件&#xff08;Outputs&#xff09; 其他 Gradio是一个Python库&#xff0c;用于构建快速的Web界面&#xff0c;以便于使用机器学习模型进行实…

Vue.js 中的 watch 属性详解

Vue.js 中的 watch 属性详解 在 Vue.js 中&#xff0c;watch 属性是一种非常重要的属性&#xff0c;它可以监听 Vue 实例中指定的数据变化&#xff0c;并在数据发生变化时执行相应的操作。本文将对 Vue.js 中的 watch 属性进行详细的介绍&#xff0c;并附上相关的代码示例。 什…

一文看穿 TypeScript 的庐山真面目

导语&#xff1a; 在了解 TypeScript 之前&#xff0c;我们需要了解 什么是强类型语言和什么是弱类型语言&#xff0c;以及什么是静态类型&#xff0c;什么又是动态类型。 强类型不允许任意的隐式类型转换&#xff0c;而 弱类型 允许静态类型&#xff1a;一个变量声明时它的类型…

基于Python的大数据舆情分析,舆论情感分析可视化系统

运行效果图 基于Python的微博大数据舆情分析&#xff0c;舆论情感分析可视化系统 系统介绍 微博舆情分析系统&#xff0c;项目后端分爬虫模块、数据分析模块、数据存储模块、业务逻辑模块组成。 先后进行了数据获取和筛选存储&#xff0c;对存储后的数据库数据进行提取分析处…

sqlmap -os-shell 使用方法

一、burp suite抓包。 如上图所示&#xff0c;红框处很明显是一个传参点&#xff0c;我们就在这个页面抓包。 抓到包之后将内容保存到桌面的1000.txt文件下。 二、sqlmap跑包。 打开sqlmap跑包。 python sqlmap.py -r C:\Users\16434\Desktop\1000.txt -dbmsmysql --os-shell…

Oracle-catalog影响归档量统计

有个12.2 rac环境报警备份异常&#xff0c;登录检查备份&#xff0c;发现报错日志 piece handle/backup/orcl/archbackup/ARCHBAK_ORCL_20230607_738_1 tagARCH_BAK commentNONE channel d1: backup set complete, elapsed time: 00:01:55 released channel: d1 RMAN-00571: …

从Vuex过渡到pinia

Vuex过渡到Pinia 众所周知&#xff0c;Vuex是一个状态管理库&#xff0c;它方便了我们任何组件不用考虑关系就可以共享一个全局的状态。&#x1f603;但是 Vuex也有它一定的缺陷。主要缺点&#xff0c;我总结如下&#xff1a; mutations里面不能写异步函数&#xff0c;否则就…

Simulink仿真模块 - Waveform Generator

Waveform Generator模块的功能是使用信号符号输出波形。它所在的库为: Simulink / Sources 如图所示: 双击模型弹出如下对话框,如图所示: Waveform Generator 模块根据您在波形定义表中输入的信号符号输出波形。 此模块支持下列用于信号符号的语法: 函数…

STM32单片机OLED语音识别路灯台灯控制系统人检测亮度调节

实践制作DIY- GC0143-OLED语音识别路灯台灯控制系统 基于STM32单片机设计---OLED语音识别路灯台灯控制系统 二、功能介绍&#xff1a; 电路&#xff1a;STM32F103C系列最小系统串口语音识别模块LED灯板1个红外传感器OLED显示器1个手动自动模式键1个开关按键 1.有两个模式1个手…

速卖通,国际站,temu测评,补单策略:安全与效能并重,提高账号存活率

测评能够帮助卖家让亚马逊平台更喜欢自己的产品&#xff0c;给予更好排名的同时也让后续进入店铺的买家更容易认可自己的产品。这些真实评价在亚马逊卖家管理系统中被称为Review Feedback。这是进行真实交易后形成的评价&#xff0c;而不是通过机器软件生成&#xff0c;形成虚拟…

SpringData进阶篇-下

SpringData进阶篇 一&#xff1a;故事背景二&#xff1a;自定义操作2.1 JPQL和SQL2.1.1 接口内定义2.1.2 调用2.2.3 SQL 方式查询 2.2 规定方法名2.2.1 普通查询规则2.2.2 修饰查询 2.3 Query By Example2.3.1 Repository继承QueryByExampleExecutor2.3.2 具体使用2.3.2 Exampl…