怎么做网站竞价推广,手机优化大师官网,网络seo是什么,重庆建造师管理系统查询系统文章目录 引入认识 YAML 格式规范定义脱敏规则格式主要逻辑实现优化方法完整工具类 引入
在项目中遇到一个需求#xff0c;需要对交易接口返回结果中的指定字段进行脱敏操作#xff0c;但又不能使用AOP注解的形式#xff0c;于是决定使用一种比较笨的方法#xff1a;
首先… 文章目录 引入认识 YAML 格式规范定义脱敏规则格式主要逻辑实现优化方法完整工具类 引入
在项目中遇到一个需求需要对交易接口返回结果中的指定字段进行脱敏操作但又不能使用AOP注解的形式于是决定使用一种比较笨的方法
首先将所有需要脱敏字段及其对应脱敏规则存储到 Map 中。在接口返回时遍历结果中的所有字段判断字段名在 Map 中是否存在 如果不存在说明该字段不需要脱敏不做处理即可。如果存在说明该字段需要脱敏从 Map 中获取对应的脱敏规则进行脱敏。 最后返回脱敏之后的结果。
认识 YAML 格式规范
由于返回的结果涉及到嵌套 Map所以决定采用 YAML 格式的文件存储脱敏规则那么为了大家统一维护和开发就需要大家对 YAML 格式进行了解遵守规范不易出错少走弯路。
YAMLYAML Ain’t Markup Language与传统的 JSON、XML 和 Properties 文件一样都是用于数据序列化的格式常用于配置文件和数据传输。
相比于其他格式YAML 是一种轻量级的数据序列化格式它的设计初衷是为了简化复杂性提高人类可读性并且易于实现和解析。 与 JSON 相比YAML 在语法上更为灵活允许使用更简洁的方式来表示数据结构。 与 XML 相比YAML 的语法更为简洁没有繁琐的标签和尖括号。 与 Properties 相比YAML 支持更复杂的数据结构包括嵌套的键值对和列表。
除此之外YAML 还支持跨平台、跨语言可以被多种编程语言解析这使得YAML非常适合用于不同语言之间的数据传输和交换。
YAML 文件的语法非常简洁明了以下是它的语法规范 基本语法 使用 缩进表示层级关系可以使用空格或制表符进行缩进但不能混用。使用冒号:表示键值对键值对之间使用换行分隔。使用破折号-表示列表项列表项之间也使用换行分隔。 # 使用缩进表示层级关系
server:port: 8080# 使用冒号表示键值对
name: John Smith
age: 30# 使用破折号表示列表项
hobbies:- reading- hiking- swimming注释 使用井号#表示注释在 # 后面的内容被视为注释可以出现在行首或行尾。 # 这是一个注释
name: John Smith
age: 30 # 这也是一个注释字符串: 字符串可以使用单引号或双引号括起来也可以不使用引号。使用双引号时可以使用转义字符如 \n 表示换行和转义序列如 \u 表示 Unicode 字符。 # 使用双引号表示字符串
name: John Smith# 使用单引号表示字符串
nickname: Johnny键值对 键值对使用冒号:表示键和值之间使用一个 空格 分隔。键可以是字符串或纯量如整数、布尔值等。值可以是字符串、纯量、列表或嵌套的键值对。 # 键和值之间使用一个空格分隔
name: John Smith# 键可以是字符串或纯量
age: 30# 值可以是字符串、纯量、列表或嵌套的键值对
address:city: San Franciscostate: Californiazip: 94107列表 使用破折号-表示列表项。列表项可以是字符串、纯量或嵌套的列表或键值对。 # 使用破折号表示列表项
hobbies:- reading- hiking- swimming# 列表项可以是字符串、纯量或嵌套的列表或键值对
people:- name: John Smithage: 30- name: Jane Doeage: 25引用 使用表示引用使用*表示引用的内容。 # 使用表示引用
address: myaddresscity: San Franciscostate: Californiazip: 94107# 使用*表示引用的内容
shippingAddress: *myaddress多行文本块 使用|保留换行符保留文本块的精确格式。使用折叠换行符将文本块折叠成一行并根据内容自动换行。 # 使用|保留换行符
description: |This is amulti-linestring.# 使用折叠换行符
summary: This is a summarythat may containline breaks.数据类型 YAML支持多种数据类型包括字符串、整数、浮点数、布尔值、日期和时间等。可以使用标记来表示一些特殊的数据类型如 !!str 表示字符串类型、!!int 表示整数类型等。 # 使用标记表示数据类型
age: !!int 30
weight: !!float 65.5
isMale: !!bool true
created: !!timestamp 2022-01-01 12:00:00多文件 可以使用—表示多个 YAML 文件之间的分隔符。每个文件可以使用任何 YAML 语法。 # 第一个YAML文件
name: John Smith
age: 30---# 第二个YAML文件
hobbies:- reading- hiking- swimming定义脱敏规则格式
对于数据结构简单的接口返回结果脱敏规则格式定义为【交易号-字段-规则】
交易号:字段名:规则: /^(1[3-9][0-9])\d{4}(\d{4}$)/同时接口返回的结果中可能用有嵌套列表那么针对这种复杂的结构就定义格式为【交易号-字段列表-字段-规则】即
交易号:字段名(列表):字段名:规则: /^(1[3-9][0-9])\d{4}(\d{4}$)/使用这种层级结构我们完全可以通过 Map.get(Key) 的形式获取到指定交易指定字段的脱敏规则。
主要逻辑实现 首先创建 YAML 文件 sensitive.yml 添加对应交易字段的脱敏规则 Y3800:phone:length: 11rule: (\\d{3})\\d{4}(\\d{4})
Y3801:idCard:length: 18rule: (?\\w{3})\\w(?\\w{4})list:email:rule: (\\w)\\w{5}(\\w)定义工具类编写我们的逻辑 public class YamlUtils {
}在 YamlUtils 工具类中我们需要实现在项目启动时读取 YAML 文件中的内容并转为我们想要的 Map 键值对数据类型 // YAML 文件路径
private static final String YAML_FILE_PATH /sensitive.yml;// 存储解析后的 YAML 数据
private static MapString, Object map;static {// 创建 Yaml 对象Yaml yaml new Yaml();// 通过 getResourceAsStream 获取 YAML 文件的输入流try (InputStream in YamlUtils.class.getResourceAsStream(YAML_FILE_PATH)) {// 解析 YAML 文件为 Map 对象map yaml.loadAs(in, Map.class);} catch (Exception e) {e.printStackTrace();}
}在上述代码中我们首先定义了一个私有静态常量 YAML_FILE_PATH用于存储 YAML 文件的路径又定义了一个静态变量 map用于存储解析后的 YAML 数据。 接着通过 getResourceAsStream 方法根据指定的 YAML 文件的路径从类路径中获取资源文件的输入流。 然后使用 loadAs 方法将输入流中的内容按照 YAML 格式进行解析并将解析结果转换为指定的 Map.class 类型。 最后使用 try-with-resources 语句来自动关闭输入流。 编写方法通过 Key 获取对应脱敏规则 public static void main(String[] args) {// 加载 YAML 文件并获取顶层的 Map 对象MapString, Object yamlMap loadYaml(/sensitive.yml);System.out.println(yamlMap);// 从顶层的 Map 中获取名为 Y3800 的嵌套 MapMapString, Object Y3800 (MapString, Object) yamlMap.get(Y3800);System.out.println(Y3800);// 从 Y3800 的嵌套 Map 中获取名为 phone 的嵌套 MapMapString, Object phone (MapString, Object) Y3800.get(phone);System.out.println(phone);
}输出结果如下 {Y3800{phone{length11, rule(\\d{3})\\d{4}(\\d{4})}}, Y3801{idCard{length18, rule(?\\w{3})\\w(?\\w{4})}, list{email{rule(\\w)\\w{5}(\\w)}}}}
{phone{length11, rule(\\d{3})\\d{4}(\\d{4})}}
{length11, rule(\\d{3})\\d{4}(\\d{4})}转为 JSON 格式显示如下 输出 YAML 文件中的全部数据 {Y3800: {phone: {length: 11,rule: (\\\\d{3})\\\\d{4}(\\\\d{4})}},Y3801: {idCard: {length: 18,rule: (?\\\\w{3})\\\\w(?\\\\w{4})},list: {email: {rule: (\\\\w)\\\\w{5}(\\\\w)}}}
}输出 Y3800 层级下的数据 {phone: {length: 11,rule: (\\\\d{3})\\\\d{4}(\\\\d{4})}
}输出 phone 层级下的数据 {length: 11,rule: (\\\\d{3})\\\\d{4}(\\\\d{4})
}文章到此需要的功能基本实现理论上该文到这里就结束了但是我们仔细思考一下我们通过 Key 获取指定层级下的数据时需要我们不断的调用 Map.get(Key) 方法即结构每嵌套一次就需要一次 getKey那么这里是否有优化的方法呢
答案是有的因为有问题就会有答案。
优化方法
优化思路为通过递归和判断来遍历嵌套的 Map直到找到键路径所对应的最里层的嵌套 Map并返回该 Map 对象。
优化后方法如下
/*** 递归获取嵌套 Map 数据** param map 嵌套数据源的 Map* param keys 嵌套键路径* return 嵌套数据对应的 Map*/
SuppressWarnings(unchecked)
public static MapString, Object getNestedMapValues(MapString, Object map, String... keys) {// 如果键路径为空或者第一个键不在 Map 中则返回 nullif (keys.length 0 || !map.containsKey(keys[0])) {return null;}// 获取第一个键对应的嵌套对象Object nestedObject map.get(keys[0]);// 如果键路径长度为 1说明已经到达最里层的嵌套 Map直接返回该 Map 对象if (keys.length 1) {if (nestedObject instanceof Map) {return (MapString, Object) nestedObject;} else {return null;}} else {// 如果嵌套对象是 Map继续递归查找下一个键的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((MapString, Object) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套对象既不是 Map 也不是 List返回 nullreturn null;}}
}调用方法时传入 Key 的嵌套路径即可
public static void main(String[] args) {// 加载 YAML 文件并获取顶层的 Map 对象MapString, Object yamlMap loadYaml(/sensitive.yml);System.out.println(yamlMap);// 获取 Y3800 - phone 下的数据转为 MapMapString, Object y3800PhoneMap YamlUtils.getNestedMap(yamlMap, Y3800, phone);System.out.println(Y3800 - phone : y3800NameMap);
}具体来说主要分为以下几步
首先判断键路径是否为空或者第一个键是否在 Map 中。如果键路径为空或者第一个键不在 Map 中则返回 null。获取第一个键对应的嵌套对象。通过 get 方法获取第一个键对应的嵌套对象。判断是否到达最里层的嵌套 Map。如果键路径长度为 1说明已经到达最里层的嵌套 Map直接返回该 Map 对象。继续递归查找下一个键的嵌套 Map。如果嵌套对象是 Map则继续递归查找下一个键的嵌套 Map。返回结果。返回递归查找的结果。
完整工具类
封装成完整的工具类如下
/*** Project demo1* ClassName YamlUtils* Description 读取YAML配置文件工具类* Author 赵士杰* Date 2024/1/26 20:15*/
SuppressWarnings(unchecked)
public class YamlUtils {// YAML 文件路径private static final String YAML_FILE_PATH /sensitive.yml;// 存储解析后的 YAML 数据private static MapString, Object map;static {// 创建 Yaml 对象Yaml yaml new Yaml();// 通过 getResourceAsStream 获取 YAML 文件的输入流try (InputStream in YamlUtils.class.getResourceAsStream(YAML_FILE_PATH)) {// 解析 YAML 文件为 Map 对象map yaml.loadAs(in, Map.class);} catch (Exception e) {e.printStackTrace();}}/*** 获取嵌套的 Map 数据** param keys 嵌套键路径* return 嵌套数据对应的 Map*/public static MapString, Object getNestedMap(String... keys) {return getNestedMapValues(map, keys);}/*** 递归获取嵌套 Map 数据** param map 嵌套数据源的 Map* param keys 嵌套键路径* return 嵌套数据对应的 Map*/public static MapString, Object getNestedMapValues(MapString, Object map, String... keys) {// 如果键路径为空或者第一个键不在 Map 中则返回 nullif (keys.length 0 || !map.containsKey(keys[0])) {return null;}// 获取第一个键对应的嵌套对象Object nestedObject map.get(keys[0]);// 如果键路径长度为 1说明已经到达最里层的嵌套 Map直接返回该 Map 对象if (keys.length 1) {if (nestedObject instanceof Map) {return (MapString, Object) nestedObject;} else {return null;}} else {// 如果嵌套对象是 Map继续递归查找下一个键的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((MapString, Object) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套对象既不是 Map 也不是 List返回 nullreturn null;}}}}