当前位置: 首页 > news >正文

【数据脱敏方案】不使用 AOP + 注解,使用 SpringBoot+YAML 实现

文章目录

    • 引入
    • 认识 YAML 格式规范
    • 定义脱敏规则格式
    • 脱敏逻辑实现
      • 读取 YAML 配置文件获取脱敏规则
      • 通过键路径获取对应字段规则
        • 原始
        • 优化后
      • 对数据进行脱敏处理
      • 递归生成字段对应的键路径
      • 脱敏测试
    • 完整工具类

引入

在项目中遇到一个需求,需要对交易接口返回结果中的指定字段进行脱敏操作,但又不能使用AOP+注解的形式,于是决定使用一种比较笨的方法:

  1. 首先将所有需要脱敏字段及其对应脱敏规则存储到 Map 中。
  2. 在接口返回时,遍历结果中的所有字段,判断字段名在 Map 中是否存在:
    • 如果不存在:说明该字段不需要脱敏,不做处理即可。
    • 如果存在:说明该字段需要脱敏,从 Map 中获取对应的脱敏规则进行脱敏。
  3. 最后返回脱敏之后的结果。

认识 YAML 格式规范

由于返回的结果涉及到嵌套 Map,所以决定采用 YAML 格式的文件存储脱敏规则,那么为了大家统一维护和开发,就需要大家对 YAML 格式进行了解,遵守规范,不易出错,少走弯路。

YAML(YAML Ain’t Markup Language)与传统的 JSON、XML 和 Properties 文件一样,都是用于数据序列化的格式,常用于配置文件和数据传输。

相比于其他格式,YAML 是一种轻量级的数据序列化格式,它的设计初衷是为了简化复杂性,提高人类可读性,并且易于实现和解析。

  • 与 JSON 相比:YAML 在语法上更为灵活,允许使用更简洁的方式来表示数据结构。

  • 与 XML 相比:YAML 的语法更为简洁,没有繁琐的标签和尖括号。

  • 与 Properties 相比:YAML 支持更复杂的数据结构,包括嵌套的键值对和列表。

除此之外,YAML 还支持跨平台、跨语言,可以被多种编程语言解析,这使得YAML非常适合用于不同语言之间的数据传输和交换。

YAML 文件的语法非常简洁明了,以下是它的语法规范:

  1. 基本语法:

    • 使用 缩进表示层级关系,可以使用空格或制表符进行缩进,但不能混用。
    • 使用冒号(:)表示键值对,键值对之间使用换行分隔。
    • 使用破折号(-)表示列表项,列表项之间也使用换行分隔。
    # 使用缩进表示层级关系
    server:port: 8080# 使用冒号表示键值对
    name: John Smith
    age: 30# 使用破折号表示列表项
    hobbies:- reading- hiking- swimming
    
  2. 注释:

    • 使用井号(#)表示注释,在 # 后面的内容被视为注释,可以出现在行首或行尾。
    # 这是一个注释
    name: John Smith
    age: 30 # 这也是一个注释
    
  3. 字符串:

    • 字符串可以使用单引号或双引号括起来,也可以不使用引号。
    • 使用双引号时,可以使用转义字符(如 \n 表示换行)和转义序列(如 \u 表示 Unicode 字符)。
    # 使用双引号表示字符串
    name: "John Smith"# 使用单引号表示字符串
    nickname: 'Johnny'
    
  4. 键值对:

    • 键值对使用冒号(:)表示,键和值之间使用一个 空格 分隔。
    • 键可以是字符串或纯量(如整数、布尔值等)。
    • 值可以是字符串、纯量、列表或嵌套的键值对。
    # 键和值之间使用一个空格分隔
    name: John Smith# 键可以是字符串或纯量
    age: 30# 值可以是字符串、纯量、列表或嵌套的键值对
    address:city: San Franciscostate: Californiazip: 94107
    
  5. 列表:

    • 使用破折号(-)表示列表项。
    • 列表项可以是字符串、纯量或嵌套的列表或键值对。
    # 使用破折号表示列表项
    hobbies:- reading- hiking- swimming# 列表项可以是字符串、纯量或嵌套的列表或键值对
    people:- name: John Smithage: 30- name: Jane Doeage: 25
    
  6. 引用:

    • 使用&表示引用,使用*表示引用的内容。
    # 使用&表示引用
    address: &myaddresscity: San Franciscostate: Californiazip: 94107# 使用*表示引用的内容
    shippingAddress: *myaddress
    
  7. 多行文本块:

    • 使用|保留换行符,保留文本块的精确格式。
    • 使用>折叠换行符,将文本块折叠成一行,并根据内容自动换行。
    # 使用|保留换行符
    description: |This is amulti-linestring.# 使用>折叠换行符
    summary: >This is a summarythat may containline breaks.
    
  8. 数据类型:

    • YAML支持多种数据类型,包括字符串、整数、浮点数、布尔值、日期和时间等。
    • 可以使用标记来表示一些特殊的数据类型,如 !!str 表示字符串类型、!!int 表示整数类型等。
    # 使用标记表示数据类型
    age: !!int 30
    weight: !!float 65.5
    isMale: !!bool true
    created: !!timestamp '2022-01-01 12:00:00'
    
  9. 多文件:

    • 可以使用—表示多个 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 配置文件获取脱敏规则

  1. 首先创建 YAML 文件 desensitize.yml 添加对应交易字段的脱敏规则:

    Y3800:phone:rule: "(\\d{3})\\d{4}(\\d{4})"format: "$1****$2"idCard:rule: "(?<=\\w{6})\\w(?=\\w{4})"format: "*"
    Y3801:idCard:rule: "(?<=\\w{3})\\w(?=\\w{4})"format: "+"list:phone:rule: "(\\d{3})\\d{4}(\\d{4})"format: "$1++++$2"
    
  2. 定义脱敏工具类 DataDesensitizationUtils 编写我们的脱敏逻辑:

    public class DataDesensitizationUtils {
    }
    
  3. DataDesensitizationUtils 工具类中,我们需要实现在项目启动时,读取 desensitize.yml 文件中的内容,并转为我们想要的 Map 键值对数据类型:

    /*** 读取yaml文件内容并转为Map* @param yamlFile yaml文件路径* @return Map对象*/
    public static Map<String, Object> loadYaml(String yamlFile) {Yaml yaml = new Yaml();try (InputStream in = DataDesensitizationUtils.class.getResourceAsStream(yamlFile)) {return yaml.loadAs(in, Map.class);} catch (Exception e) {e.printStackTrace();}return null;
    }
    

    在上述代码中,我们通过 getResourceAsStream 方法根据指定的 YAML 文件的路径从类路径中获取资源文件的输入流。

    然后使用 loadAs 方法将输入流中的内容按照 YAML 格式进行解析,并将解析结果转换为指定的 Map.class 类型。

    最后使用 try-with-resources 语句来自动关闭输入流。

通过键路径获取对应字段规则

原始
  1. 在上文中我们已经将 desensitize.yml 文件中所有的脱敏规则都以 key-Value 的形式存储到了 Map 中,因此我们只需要通过 Key 从 Map 中获取即可。接下来编写方法通过 Key 获取指定字段对应脱敏规则:

    public static void main(String[] args) {// 加载 YAML 文件并获取顶层的 Map 对象,路径基于 resources 目录Map<String, Object> yamlMap = loadYaml("/desensitize.yml");System.out.println(yamlMap);// 从顶层的 Map 中获取名为 "Y3800" 的嵌套 MapMap<String, Object> Y3800= (Map<String, Object>) yamlMap.get("Y3800");System.out.println(Y3800);// 从 "Y3800" 的嵌套 Map 中获取名为 "phone" 的嵌套 MapMap<String, Object> phone = (Map<String, Object>) Y3800.get("phone");System.out.println(phone);
    }
    

    输出结果如下:

    {Y3800={phone={rule=(\d{3})\d{4}(\d{4}), format=$1****$2}, idCard={rule=(?<=\w{3})\w(?=\w{4}), format=*}}, Y3801={name={rule=.(?=.), format=+}, idCard={rule=(?<=\w{3})\w(?=\w{4}), format=+}, list={card={rule=\d(?=\d{4}), format=+}}}}
    {phone={rule=(\d{3})\d{4}(\d{4}), format=$1****$2}, idCard={rule=(?<=\w{3})\w(?=\w{4}), format=*}}
    {rule=(\d{3})\d{4}(\d{4}), format=$1****$2}
    

    转为 JSON 格式显示如下:

    • 输出 YAML 文件中的全部数据:

      {"Y3800": {"phone": {"rule": "(\\d{3})\\d{4}(\\d{4})","format": "$1****$2"},"idCard": {"rule": "(?<=\\w{3})\\w(?=\\w{4})","format": "*"}},"Y3801": {"name": {"rule": ".(?=.)","format": "+"},"idCard": {"rule": "(?<=\\w{3})\\w(?=\\w{4})","format": "+"},"list": {"card": {"rule": "\\d(?=\\d{4})","format": "+"}}}
      }
      
    • 输出 Y3800 层级下的数据:

      {"phone": {"rule": "(\\d{3})\\d{4}(\\d{4})","format": "$1****$2"},"idCard": {"rule": "(?<=\\w{3})\\w(?=\\w{4})","format": "*"}
      }
      
    • 输出 phone 层级下的数据:

      {"rule": "(\\d{3})\\d{4}(\\d{4})","format": "$1****$2"
      }
      

在这里,我们需要仔细思考一下,在我们通过 Key 获取指定层级下的数据时,我们需要不断的调用 Map.get("Key") 方法,即结构每嵌套一次,就需要一次 getKey,那么这里是否有优化的方法呢?

答案是:有的,因为有问题就会有答案。

优化后

首先我们需要先了解一个概念:

Y3800:phone:rule: "(\\d{3})\\d{4}(\\d{4})"format: "$1****$2"

当我们要从上述数据中获取 phone 的脱敏规则时,我们需要先从 Map 中 get("Y3800") 获取 Y3800 下的数据,再通过 get("phone") 获取 phone 下的规则,那么 Y3800->phone 就是 phone 的键路径。

基于此,我们可以实现这样一个方法,我们直接给出指定字段的键路径,在方法中通过递归的方式从 Map 中获取到该键路径下的所有数据,然后返回即可。

即优化思路为:通过递归和判断来遍历嵌套的 Map,直到找到键路径所对应的最里层的嵌套 Map,并返回该 Map 对象。

优化后方法如下:

/*** 递归获取嵌套 Map 数据** @param map  嵌套数据源的 Map* @param keys 嵌套键路径* @return 嵌套数据对应的 Map*/
@SuppressWarnings("unchecked")
public static Map<String, Object> getNestedMapValues(Map<String, 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 (Map<String, Object>) nestedObject;} else {return null;}} else {// 如果嵌套对象是 Map,继续递归查找下一个键的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((Map<String, Object>) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套对象既不是 Map 也不是 List,返回 nullreturn null;}}
}

调用方法时传入 Key 的嵌套路径即可:

public static void main(String[] args) {// 加载 YAML 文件并获取顶层的 Map 对象Map<String, Object> yamlMap = loadYaml("/desensitize.yml");System.out.println(yamlMap);// 获取 Y3800 -> phone 下的数据转为 MapMap<String, Object> y3800PhoneMap = YamlUtils.getNestedMap(yamlMap, "Y3800", "phone");System.out.println("Y3800 -> phone : " + y3800NameMap);
}

具体来说,主要分为以下几步:

  1. 首先判断键路径是否为空或者第一个键是否在 Map 中。如果键路径为空或者第一个键不在 Map 中,则返回 null。
  2. 获取第一个键对应的嵌套对象。通过 get 方法获取第一个键对应的嵌套对象。
  3. 判断是否到达最里层的嵌套 Map。如果键路径长度为 1,说明已经到达最里层的嵌套 Map,直接返回该 Map 对象。
  4. 继续递归查找下一个键的嵌套 Map。如果嵌套对象是 Map,则继续递归查找下一个键的嵌套 Map。
  5. 返回结果。返回递归查找的结果。

对数据进行脱敏处理

获取到字段的脱敏规则后,我们就可以编写方法实现对源数据做脱敏处理,脱敏方法如下:

/*** 使用指定规则对数据进行脱敏处理** @param data 要进行脱敏处理的数据* @param map 包含脱敏规则和格式的参数映射*            - "rule" 表示脱敏规则的正则表达式*            - "format" 表示替换脱敏部分的字符串,默认为 "*"* @return 脱敏后的数据*/
private static String desensitizeLogic(String data, Map<String, Object> map) {if (map.containsKey("rule")) {String rule = (String) map.get("rule");String sign = "*";if (map.containsKey("format")) {sign = (String) map.get("format");}return data.replaceAll(rule, sign);}return data;
}

递归生成字段对应的键路径

目前我们已经实现了通过字段的键路径获取到该字段对应规则的方法 getNestedMapValues(),那么接下来我们只需要生成字段对应的键路径,然后调用方法 getNestedMapValues() 获取到脱敏规则后调用 desensitizeLogic() 对源数据进行脱敏即可。

提供源数据格式如下:

{"txEntity": {"idCard": "130428197001180384","name": "赵士杰","list": [{"phone": "17631007015"},{"phone": "17631007015"}]},"txHeader": {"servNo": "Y3801"}
}

根据上述数据结构,首先我们需要从 txHeader 中获取 servNo,之后递归遍历 txEntity 中的元素即可。

具体方法如下:

/*** 对指定实体数据进行脱敏处理** @param entity 要进行脱敏处理的实体数据* @param servNo 当前交易的服务号,用于记录日志* @param path 当前实体数据在整个数据结构中的路径,用于记录日志*/
public static void parseData(Object entity, String servNo, String path) {if (entity instanceof Map) {for (Map.Entry<String, Object> entry : ((Map<String, Object>) entity).entrySet()) {// 计算当前键值对在整个数据结构中的路径String currentPath = path.isEmpty() ? entry.getKey() : path + "," + entry.getKey();if (entry.getValue() instanceof Map) {// 如果当前值是 Map 类型,则递归处理子节点parseData(entry.getValue(), servNo, currentPath);} else if (entry.getValue() instanceof List) {// 如果当前值是 List 类型,则遍历列表中的每个元素并递归处理子节点for (Object item : (List) entry.getValue()) {if (item instanceof Map) {parseData(item, servNo, currentPath);}}} else {// 如果当前值不是 Map 或 List,则进行脱敏处理String p = servNo + "," +currentPath;String[] keyPaths = p.split(",");// 获取当前节点的脱敏规则和格式Map<String, Object> nestedMap = getNestedMap(keyPaths);if(Objects.nonNull(nestedMap)){// 记录日志log.info("-----------------交易【{}】,字段【{}】开始脱敏-----------------",servNo,currentPath.replace(",","->"));log.info("原始值:【{}:{}】",entry.getKey(),entry.getValue());log.info("脱敏规则:{}",nestedMap);// 对当前节点的值进行脱敏处理String desensitized = desensitizeLogic((String) entry.getValue(), nestedMap);entry.setValue(desensitized);// 记录日志log.info("脱敏值:【{}:{}】",entry.getKey(),entry.getValue());log.info("-----------------交易【{}】,字段【{}】脱敏结束-----------------",servNo,currentPath.replace(",","->"));}}}}
}

该方法接收一个实体数据 entity,一个服务号 servNo 和一个路径 path 作为参数。在方法体内,会遍历实体数据的键值对,并根据具体情况递归处理子节点或进行脱敏处理。

  • 当实体数据的值为 Map 类型时,方法会递归处理子节点;
  • 当值为 List 类型时,方法会遍历列表中的每个元素并递归处理子节点;
  • 当值既不是 Map 也不是 List 时,方法会根据服务号和路径获取脱敏规则,并对当前节点的值进行脱敏处理,并记录脱敏日志。

脱敏处理的具体逻辑和规则通过调用 getNestedMap 方法和 desensitizeLogic 方法来实现,其中 getNestedMap 方法用于获取脱敏规则,desensitizeLogic 方法用于根据脱敏规则对数据进行脱敏处理。

注:请注意本文中提供的数据样例的层次结构是和 YAML 中定义的结构是一样的,再通过上述方法递归后生成的键路径是和从 YAML 中获取规则所需的键路径是一致的,因此可以直接调用 getNestedMapValues() 获取脱敏规则。在实际使用中,其他数据结构需要重写该逻辑。

脱敏测试

编写 Main 方法调用:

public class Demo {public static Map<String, Object> getData() {HashMap<String, Object> phone = new HashMap<>();phone.put("phone", "17631007015");HashMap<String, Object> phone2 = new HashMap<>();phone2.put("phone", "17631007015");List<HashMap<String, Object>> list = new ArrayList<>();list.add(phone);list.add(phone2);HashMap<String, Object> txEntity = new HashMap<>();txEntity.put("name", "赵士杰");txEntity.put("idCard", "130428197001180384");txEntity.put("list", list);HashMap<String, Object> result = new HashMap<>();result.put("txEntity", txEntity);HashMap<String, Object> txHeader = new HashMap<>();txHeader.put("servNo", "Y3801");result.put("txHeader", txHeader);return result;}public static void main(String[] args) {Map<String, Object> data = getData();// 假设data中包含接口返回的数据if (data.containsKey("txHeader") && data.get("txHeader") instanceof Map) {String servNo = ((Map<String, String>) data.get("txHeader")).get("servNo");DataDesensitizationUtils.parseData(data.get("txEntity"), servNo, "");}}}

运行测试,控制台输出如下:

-----------------交易【Y3801】,字段【idCard】开始脱敏-----------------
原始值:【idCard:130428197001180384】
脱敏规则:{rule=(?<=\w{3})\w(?=\w{4}), format=+}
脱敏值:【idCard:130+++++++++++0384】
-----------------交易【Y3801】,字段【idCard】脱敏结束-----------------
-----------------交易【Y3801】,字段【list->phone】开始脱敏-----------------
原始值:【phone:17631007015】
脱敏规则:{rule=(\d{3})\d{4}(\d{4}), format=$1++++$2}
脱敏值:【phone:176++++7015】
-----------------交易【Y3801】,字段【list->phone】脱敏结束-----------------
-----------------交易【Y3801】,字段【list->phone】开始脱敏-----------------
原始值:【phone:17631007015】
脱敏规则:{rule=(\d{3})\d{4}(\d{4}), format=$1++++$2}
脱敏值:【phone:176++++7015】
-----------------交易【Y3801】,字段【list->phone】脱敏结束-----------------

数据脱敏后如下:

{"txEntity": {"idCard": "130+++++++++++0384","name": "赵士杰","list": [{"phone": "176++++7015"},{"phone": "176++++7015"}]},"txHeader": {"servNo": "Y3801"}
}

完整工具类

封装成完整的工具类如下:

/*** @ClassName DataDesensitizationUtils* @Description 数据脱敏工具类* @Author 赵士杰* @Date 2024/1/25 20:15*/
@Slf4j
@SuppressWarnings("unchecked")
public class DataDesensitizationUtils {// YAML 文件路径private static final String YAML_FILE_PATH = "/tuomin.yml";// 存储解析后的 YAML 数据private static Map<String, Object> map;static {// 创建 Yaml 对象Yaml yaml = new Yaml();// 通过 getResourceAsStream 获取 YAML 文件的输入流try (InputStream in = DataDesensitizationUtils.class.getResourceAsStream(YAML_FILE_PATH)) {// 解析 YAML 文件为 Map 对象map = yaml.loadAs(in, Map.class);} catch (Exception e) {e.printStackTrace();}}/*** 获取嵌套的 Map 数据** @param keys 嵌套键路径* @return 嵌套数据对应的 Map*/private static Map<String, Object> getNestedMap(String... keys) {return getNestedMapValues(map, keys);}/*** 递归获取嵌套 Map 数据** @param map  嵌套数据源的 Map* @param keys 嵌套键路径* @return 嵌套数据对应的 Map*/private static Map<String, Object> getNestedMapValues(Map<String, 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 (Map<String, Object>) nestedObject;} else {return null;}} else {// 如果嵌套对象是 Map,继续递归查找下一个键的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((Map<String, Object>) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套对象既不是 Map 也不是 List,返回 nullreturn null;}}}/*** 对指定实体数据进行脱敏处理** @param entity 要进行脱敏处理的实体数据* @param servNo 当前交易的服务号,用于记录日志* @param path 当前实体数据在整个数据结构中的路径,用于记录日志*/public static void parseData(Object entity, String servNo, String path) {if (entity instanceof Map) {for (Map.Entry<String, Object> entry : ((Map<String, Object>) entity).entrySet()) {String currentPath = path.isEmpty() ? entry.getKey() : path + "," + entry.getKey();if (entry.getValue() instanceof Map) {parseData(entry.getValue(), servNo, currentPath);} else if (entry.getValue() instanceof List) {for (Object item : (List) entry.getValue()) {if (item instanceof Map) {parseData(item, servNo, currentPath);}}} else {String p = servNo + "," + currentPath;String[] keyPaths = p.split(",");Map<String, Object> nestedMap = getNestedMap(keyPaths);if (Objects.nonNull(nestedMap)) {log.info("-----------------交易【{}】,字段【{}】开始脱敏-----------------", servNo, currentPath.replace(",", "->"));log.info("原始值:【{}:{}】", entry.getKey(), entry.getValue());log.info("脱敏规则:{}", nestedMap);String desensitized = desensitizeLogic((String) entry.getValue(), nestedMap);entry.setValue(desensitized);log.info("脱敏值:【{}:{}】", entry.getKey(), entry.getValue());log.info("-----------------交易【{}】,字段【{}】脱敏结束-----------------", servNo, currentPath.replace(",", "->"));}}}}}/*** 脱敏逻辑* @param data 源数据* @param map 脱敏规则* @return 脱敏后的数据*/private static String desensitizeLogic(String data, Map<String, Object> map) {if (map.containsKey("rule")) {String rule = (String) map.get("rule");String sign = "*";if (map.containsKey("format")) {sign = (String) map.get("format");}return data.replaceAll(rule, sign);}return data;}}

相关文章:

【数据脱敏方案】不使用 AOP + 注解,使用 SpringBoot+YAML 实现

文章目录 引入认识 YAML 格式规范定义脱敏规则格式脱敏逻辑实现读取 YAML 配置文件获取脱敏规则通过键路径获取对应字段规则原始优化后 对数据进行脱敏处理递归生成字段对应的键路径脱敏测试 完整工具类 引入 在项目中遇到一个需求&#xff0c;需要对交易接口返回结果中的指定…...

dbt doc 生成文档命令示例应用

DBT提供了强大的命令行工具&#xff0c;它使数据分析师和工程师能够更有效地转换仓库中的数据。dbt的一个关键特性是能够为数据模型生成文档&#xff0c;这就是dbt docs命令发挥作用的地方。本教程将指导您完成使用dbt生成和提供项目文档的过程。 dbt doc 命令 dbt docs命令有…...

【Windows】【DevOps】Windows Server 2022 安装ansible,基于powershell实现远程自动化运维部署 入门到放弃!

目标服务器安装openssh server参考 【Windows】【DevOps】Windows Server 2022 在线/离线 安装openssh实现ssh远程登陆powershell、scp文件拷贝-CSDN博客 注意&#xff1a;Ansible不支持Windows操作系统部署 根据官方说明&#xff1a; Windows Frequently Asked Questions —…...

深入理解 Parquet 文件格式

深入理解 Parquet 文件格式 深入理解 Parquet 文件格式一、引言二、为什么采用 Parquet 格式1. 行式存储的局限性2. 列式存储的优势 三、Parquet 的工作原理1. 文件结构2. 列块和页面3. 编码和压缩 四、具体数据实例1. 数据示例2. 行式存储 vs 列式存储3. 查询性能对比4. 压缩效…...

计算机挑战赛3

老式的计算机只能按照固定次序进行运算&#xff0c;华安大学就有这样一台老式计算机&#xff0c;计算模式为AB#C&#xff0c;和#为输入的运算符(可能是、-或*&#xff0c;运算符优先级与C一致)&#xff0c;现给出A&#xff0c;B&#xff0c;C的数值以及和#对应的运算符&#xf…...

深度学习:循环神经网络—RNN的原理

传统神经网络存在的问题&#xff1f; 无法训练出具有顺序的数据。模型搭建时没有考虑数据上下之间的关系。 RNN神经网络 RNN&#xff08;Recurrent Neural Network&#xff0c;循环神经网络&#xff09;是一种专门用于处理序列数据的神经网络。在处理序列输入时具有记忆性…...

蓝桥杯刷题--幸运数字

幸运数字 题目: 解析: 我们由题目可以知道,某个进制的哈沙德数就是该数和各个位的和取整为0.然后一个幸运数字就是满足所有进制的哈沙德数之和.然后具体就是分为以下几个步骤 1. 我们先写一个方法,里面主要是用来判断,这个数在该进制下是否是哈沙德数 2. 我们在main方法里面调用…...

Node.js入门——fs、path模块、URL端口号、模块化导入导出、包、npm软件包管理器

Node.js入门 1.介绍 定义&#xff1a;跨平台的JS运行环境&#xff0c;使开发者可以搭建服务器端的JS应用程序作用&#xff1a;使用Node.Js编写服务器端代码Node.js是基于Chrome V8引擎进行封装&#xff0c;Node中没有BOM和DOM 2.fs模块-读写文件 定义&#xff1a;封装了与…...

多元线性回归:机器学习中的经典模型探讨

引言 多元线性回归是统计学和机器学习中广泛应用的一种回归分析方法。它通过分析多个自变量与因变量之间的关系&#xff0c;帮助我们理解和预测数据的行为。本文将深入探讨多元线性回归的理论背景、数学原理、模型构建、技术细节及其实际应用。 一、多元线性回归的背景与发展…...

域1:安全与风险管理 第1章实现安全治理的原则和策略

---包括OSG 1、2、3、4 章--- 第1章、实现安全治理的原则和策略 1、由保密性、完整性和可用性组成的 CIA 三元组。 保密性原则是指客体不会被泄露给 未经授权的主体。完整性原则是指客体保持真实性且只被经过授权的主体进行有目的的修改。 可用性原则指被授权的主体能实时和…...

【大数据】学习大数据开发应该从哪些技术栈开始学习?

学习大数据开发可以从以下几个技术栈和阶段入手。以下内容按学习顺序和重要性列出&#xff0c;帮助你逐步掌握大数据开发的核心技能&#xff1a; 1. 编程基础 Java&#xff1a;Hadoop 和许多大数据工具&#xff08;如 Spark、Flink&#xff09;的核心代码都是用 Java 编写的&…...

CentOS快速配置网络Docker快速部署

CentOS快速配置网络&&Docker快速部署 CentOS裸机Docker部署1.联通外网2.配置CentOS镜像源3.安装Docker4.启动Docker5.CentOS7安装DockerCompose Bug合集ERROR [internal] load metadata for docker.io/library/java:8-alpineError: Could not find or load main class …...

Grounded-SAM Demo部署搭建

目录 1 环境部署 2 Grounded-SAM Demo安装 3 运行Demo 3.1 运行Gradio APP 3.2 Gradio APP操作 1 环境部署 由于SAM建议使用CUDA 11.3及以上版本&#xff0c;这里使用CUDA 11.4版本。 另外&#xff0c;由于整个SAM使用的是Pytorch开发&#xff0c;因此需要Python环境&…...

C语言 | 第十六章 | 共用体 家庭收支软件-1

P 151 结构体定义三种形式 2023/3/15 一、创建结构体和结构体变量 方式1-先定义结构体&#xff0c;然后再创建结构体变量。 struct Stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在学习小组 float score; //成绩 }; struct Stu stu1, stu2; //…...

【论文阅读】Learning a Few-shot Embedding Model with Contrastive Learning

使用对比学习来学习小样本嵌入模型 引用&#xff1a;Liu, Chen, et al. “Learning a few-shot embedding model with contrastive learning.” Proceedings of the AAAI conference on artificial intelligence. Vol. 35. No. 10. 2021. 论文地址&#xff1a;下载地址 论文代码…...

OKHTTP 如何处理请求超时和重连机制

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…...

基于Springboot vue的流浪狗领养管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…...

爬虫案例——网易新闻数据的爬取

案例需求&#xff1a; 1.爬取该新闻网站——&#xff08;网易新闻&#xff09;的数据&#xff0c;包括标题和链接 2.爬取所有数据&#xff08;翻页参数&#xff09; 3.利用jsonpath解析数据 分析&#xff1a; 该网站属于异步加载网站——直接网页中拿不到&#xff0c;需要…...

SpringCloud 2023 Gateway的Filter配置介绍、类型、内置过滤器、自定义全局和单一内置过滤器

目录 1. Filter介绍2. Filter类型3. 内置过滤器3.1 请求头(RequestHeader)相关GatewayFilter Factory3.2 请求参数(RequestParameter)相关GatewayFilter Factory3.3 回应头(ResponseHeader)相关GatewayFilter Factory3.4 前缀和路径相关GatewayFilter Factory3.5 Default Filte…...

从银幕到现实:擎天柱机器人即将改变我们的生活

擎天柱&#xff08;Optimus&#xff09;是《变形金刚》系列电影中的主角&#xff0c;如今也成为特斯拉正在开发的通用机器人。2022年10月&#xff0c;特斯拉展示了这一机器人的初始版本&#xff0c;创始人埃隆马斯克表示&#xff0c;希望到2023年能够启动生产。他指出&#xff…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...