文章目录
- 一、简介
- 二、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
简单介绍以下 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.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
下面是简单实现
自定义 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,在 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
有可能理解不到位,还需要重写其他方法,目前运行正常,后面遇到问题在追加