中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

政府網(wǎng)站建設(shè)的存在問題網(wǎng)址查詢工具

政府網(wǎng)站建設(shè)的存在問題,網(wǎng)址查詢工具,網(wǎng)站首頁圖片分辨率,怎么做網(wǎng)站推廣臨沂文章目錄 引入認(rèn)識(shí) YAML 格式規(guī)范定義脫敏規(guī)則格式脫敏邏輯實(shí)現(xiàn)讀取 YAML 配置文件獲取脫敏規(guī)則通過鍵路徑獲取對(duì)應(yīng)字段規(guī)則原始優(yōu)化后 對(duì)數(shù)據(jù)進(jìn)行脫敏處理遞歸生成字段對(duì)應(yīng)的鍵路徑脫敏測(cè)試 完整工具類 引入 在項(xiàng)目中遇到一個(gè)需求,需要對(duì)交易接口返回結(jié)果中的指定…

文章目錄

    • 引入
    • 認(rèn)識(shí) YAML 格式規(guī)范
    • 定義脫敏規(guī)則格式
    • 脫敏邏輯實(shí)現(xiàn)
      • 讀取 YAML 配置文件獲取脫敏規(guī)則
      • 通過鍵路徑獲取對(duì)應(yīng)字段規(guī)則
        • 原始
        • 優(yōu)化后
      • 對(duì)數(shù)據(jù)進(jìn)行脫敏處理
      • 遞歸生成字段對(duì)應(yīng)的鍵路徑
      • 脫敏測(cè)試
    • 完整工具類

引入

在項(xiàng)目中遇到一個(gè)需求,需要對(duì)交易接口返回結(jié)果中的指定字段進(jìn)行脫敏操作,但又不能使用AOP+注解的形式,于是決定使用一種比較笨的方法:

  1. 首先將所有需要脫敏字段及其對(duì)應(yīng)脫敏規(guī)則存儲(chǔ)到 Map 中。
  2. 在接口返回時(shí),遍歷結(jié)果中的所有字段,判斷字段名在 Map 中是否存在:
    • 如果不存在:說明該字段不需要脫敏,不做處理即可。
    • 如果存在:說明該字段需要脫敏,從 Map 中獲取對(duì)應(yīng)的脫敏規(guī)則進(jìn)行脫敏。
  3. 最后返回脫敏之后的結(jié)果。

認(rèn)識(shí) YAML 格式規(guī)范

由于返回的結(jié)果涉及到嵌套 Map,所以決定采用 YAML 格式的文件存儲(chǔ)脫敏規(guī)則,那么為了大家統(tǒng)一維護(hù)和開發(fā),就需要大家對(duì) YAML 格式進(jìn)行了解,遵守規(guī)范,不易出錯(cuò),少走彎路。

YAML(YAML Ain’t Markup Language)與傳統(tǒng)的 JSON、XML 和 Properties 文件一樣,都是用于數(shù)據(jù)序列化的格式,常用于配置文件和數(shù)據(jù)傳輸。

相比于其他格式,YAML 是一種輕量級(jí)的數(shù)據(jù)序列化格式,它的設(shè)計(jì)初衷是為了簡(jiǎn)化復(fù)雜性,提高人類可讀性,并且易于實(shí)現(xiàn)和解析。

  • 與 JSON 相比:YAML 在語法上更為靈活,允許使用更簡(jiǎn)潔的方式來表示數(shù)據(jù)結(jié)構(gòu)。

  • 與 XML 相比:YAML 的語法更為簡(jiǎn)潔,沒有繁瑣的標(biāo)簽和尖括號(hào)。

  • 與 Properties 相比:YAML 支持更復(fù)雜的數(shù)據(jù)結(jié)構(gòu),包括嵌套的鍵值對(duì)和列表。

除此之外,YAML 還支持跨平臺(tái)、跨語言,可以被多種編程語言解析,這使得YAML非常適合用于不同語言之間的數(shù)據(jù)傳輸和交換。

YAML 文件的語法非常簡(jiǎn)潔明了,以下是它的語法規(guī)范:

  1. 基本語法:

    • 使用 縮進(jìn)表示層級(jí)關(guān)系,可以使用空格或制表符進(jìn)行縮進(jìn),但不能混用。
    • 使用冒號(hào)(:)表示鍵值對(duì),鍵值對(duì)之間使用換行分隔。
    • 使用破折號(hào)(-)表示列表項(xiàng),列表項(xiàng)之間也使用換行分隔。
    # 使用縮進(jìn)表示層級(jí)關(guān)系
    server:port: 8080# 使用冒號(hào)表示鍵值對(duì)
    name: John Smith
    age: 30# 使用破折號(hào)表示列表項(xiàng)
    hobbies:- reading- hiking- swimming
    
  2. 注釋:

    • 使用井號(hào)(#)表示注釋,在 # 后面的內(nèi)容被視為注釋,可以出現(xiàn)在行首或行尾。
    # 這是一個(gè)注釋
    name: John Smith
    age: 30 # 這也是一個(gè)注釋
    
  3. 字符串:

    • 字符串可以使用單引號(hào)或雙引號(hào)括起來,也可以不使用引號(hào)。
    • 使用雙引號(hào)時(shí),可以使用轉(zhuǎn)義字符(如 \n 表示換行)和轉(zhuǎn)義序列(如 \u 表示 Unicode 字符)。
    # 使用雙引號(hào)表示字符串
    name: "John Smith"# 使用單引號(hào)表示字符串
    nickname: 'Johnny'
    
  4. 鍵值對(duì):

    • 鍵值對(duì)使用冒號(hào)(:)表示,鍵和值之間使用一個(gè) 空格 分隔。
    • 鍵可以是字符串或純量(如整數(shù)、布爾值等)。
    • 值可以是字符串、純量、列表或嵌套的鍵值對(duì)。
    # 鍵和值之間使用一個(gè)空格分隔
    name: John Smith# 鍵可以是字符串或純量
    age: 30# 值可以是字符串、純量、列表或嵌套的鍵值對(duì)
    address:city: San Franciscostate: Californiazip: 94107
    
  5. 列表:

    • 使用破折號(hào)(-)表示列表項(xiàng)。
    • 列表項(xiàng)可以是字符串、純量或嵌套的列表或鍵值對(duì)。
    # 使用破折號(hào)表示列表項(xiàng)
    hobbies:- reading- hiking- swimming# 列表項(xiàng)可以是字符串、純量或嵌套的列表或鍵值對(duì)
    people:- name: John Smithage: 30- name: Jane Doeage: 25
    
  6. 引用:

    • 使用&表示引用,使用*表示引用的內(nèi)容。
    # 使用&表示引用
    address: &myaddresscity: San Franciscostate: Californiazip: 94107# 使用*表示引用的內(nèi)容
    shippingAddress: *myaddress
    
  7. 多行文本塊:

    • 使用|保留換行符,保留文本塊的精確格式。
    • 使用>折疊換行符,將文本塊折疊成一行,并根據(jù)內(nèi)容自動(dòng)換行。
    # 使用|保留換行符
    description: |This is amulti-linestring.# 使用>折疊換行符
    summary: >This is a summarythat may containline breaks.
    
  8. 數(shù)據(jù)類型:

    • YAML支持多種數(shù)據(jù)類型,包括字符串、整數(shù)、浮點(diǎn)數(shù)、布爾值、日期和時(shí)間等。
    • 可以使用標(biāo)記來表示一些特殊的數(shù)據(jù)類型,如 !!str 表示字符串類型、!!int 表示整數(shù)類型等。
    # 使用標(biāo)記表示數(shù)據(jù)類型
    age: !!int 30
    weight: !!float 65.5
    isMale: !!bool true
    created: !!timestamp '2022-01-01 12:00:00'
    
  9. 多文件:

    • 可以使用—表示多個(gè) YAML 文件之間的分隔符。每個(gè)文件可以使用任何 YAML 語法。
    # 第一個(gè)YAML文件
    name: John Smith
    age: 30---# 第二個(gè)YAML文件
    hobbies:- reading- hiking- swimming
    

定義脫敏規(guī)則格式

對(duì)于數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單的接口返回結(jié)果,脫敏規(guī)則格式定義為【交易號(hào)->字段->規(guī)則】:

交易號(hào):字段名:規(guī)則: '/^(1[3-9][0-9])\d{4}(\d{4}$)/'

同時(shí)接口返回的結(jié)果中可能用有嵌套列表,那么針對(duì)這種復(fù)雜的結(jié)構(gòu)就定義格式為【交易號(hào)->字段(列表)->字段->規(guī)則】,即:

交易號(hào):字段名(列表):字段名:規(guī)則: '/^(1[3-9][0-9])\d{4}(\d{4}$)/'

使用這種層級(jí)結(jié)構(gòu),我們完全可以通過 Map.get("Key") 的形式獲取到指定交易,指定字段的脫敏規(guī)則。

脫敏邏輯實(shí)現(xiàn)

讀取 YAML 配置文件獲取脫敏規(guī)則

  1. 首先創(chuàng)建 YAML 文件 desensitize.yml 添加對(duì)應(yīng)交易字段的脫敏規(guī)則:

    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 工具類中,我們需要實(shí)現(xiàn)在項(xiàng)目啟動(dòng)時(shí),讀取 desensitize.yml 文件中的內(nèi)容,并轉(zhuǎn)為我們想要的 Map 鍵值對(duì)數(shù)據(jù)類型:

    /*** 讀取yaml文件內(nèi)容并轉(zhuǎn)為Map* @param yamlFile yaml文件路徑* @return Map對(duì)象*/
    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 方法根據(jù)指定的 YAML 文件的路徑從類路徑中獲取資源文件的輸入流。

    然后使用 loadAs 方法將輸入流中的內(nèi)容按照 YAML 格式進(jìn)行解析,并將解析結(jié)果轉(zhuǎn)換為指定的 Map.class 類型。

    最后使用 try-with-resources 語句來自動(dòng)關(guān)閉輸入流。

通過鍵路徑獲取對(duì)應(yīng)字段規(guī)則

原始
  1. 在上文中我們已經(jīng)將 desensitize.yml 文件中所有的脫敏規(guī)則都以 key-Value 的形式存儲(chǔ)到了 Map 中,因此我們只需要通過 Key 從 Map 中獲取即可。接下來編寫方法通過 Key 獲取指定字段對(duì)應(yīng)脫敏規(guī)則:

    public static void main(String[] args) {// 加載 YAML 文件并獲取頂層的 Map 對(duì)象,路徑基于 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);
    }
    

    輸出結(jié)果如下:

    {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}
    

    轉(zhuǎn)為 JSON 格式顯示如下:

    • 輸出 YAML 文件中的全部數(shù)據(jù):

      {"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 層級(jí)下的數(shù)據(jù):

      {"phone": {"rule": "(\\d{3})\\d{4}(\\d{4})","format": "$1****$2"},"idCard": {"rule": "(?<=\\w{3})\\w(?=\\w{4})","format": "*"}
      }
      
    • 輸出 phone 層級(jí)下的數(shù)據(jù):

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

在這里,我們需要仔細(xì)思考一下,在我們通過 Key 獲取指定層級(jí)下的數(shù)據(jù)時(shí),我們需要不斷的調(diào)用 Map.get("Key") 方法,即結(jié)構(gòu)每嵌套一次,就需要一次 getKey,那么這里是否有優(yōu)化的方法呢?

答案是:有的,因?yàn)橛袉栴}就會(huì)有答案。

優(yōu)化后

首先我們需要先了解一個(gè)概念:

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

當(dāng)我們要從上述數(shù)據(jù)中獲取 phone 的脫敏規(guī)則時(shí),我們需要先從 Map 中 get("Y3800") 獲取 Y3800 下的數(shù)據(jù),再通過 get("phone") 獲取 phone 下的規(guī)則,那么 Y3800->phone 就是 phone 的鍵路徑。

基于此,我們可以實(shí)現(xiàn)這樣一個(gè)方法,我們直接給出指定字段的鍵路徑,在方法中通過遞歸的方式從 Map 中獲取到該鍵路徑下的所有數(shù)據(jù),然后返回即可。

即優(yōu)化思路為:通過遞歸和判斷來遍歷嵌套的 Map,直到找到鍵路徑所對(duì)應(yīng)的最里層的嵌套 Map,并返回該 Map 對(duì)象。

優(yōu)化后方法如下:

/*** 遞歸獲取嵌套 Map 數(shù)據(jù)** @param map  嵌套數(shù)據(jù)源的 Map* @param keys 嵌套鍵路徑* @return 嵌套數(shù)據(jù)對(duì)應(yīng)的 Map*/
@SuppressWarnings("unchecked")
public static Map<String, Object> getNestedMapValues(Map<String, Object> map, String... keys) {// 如果鍵路徑為空或者第一個(gè)鍵不在 Map 中,則返回 nullif (keys.length == 0 || !map.containsKey(keys[0])) {return null;}// 獲取第一個(gè)鍵對(duì)應(yīng)的嵌套對(duì)象Object nestedObject = map.get(keys[0]);// 如果鍵路徑長(zhǎng)度為 1,說明已經(jīng)到達(dá)最里層的嵌套 Map,直接返回該 Map 對(duì)象if (keys.length == 1) {if (nestedObject instanceof Map) {return (Map<String, Object>) nestedObject;} else {return null;}} else {// 如果嵌套對(duì)象是 Map,繼續(xù)遞歸查找下一個(gè)鍵的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((Map<String, Object>) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套對(duì)象既不是 Map 也不是 List,返回 nullreturn null;}}
}

調(diào)用方法時(shí)傳入 Key 的嵌套路徑即可:

public static void main(String[] args) {// 加載 YAML 文件并獲取頂層的 Map 對(duì)象Map<String, Object> yamlMap = loadYaml("/desensitize.yml");System.out.println(yamlMap);// 獲取 Y3800 -> phone 下的數(shù)據(jù)轉(zhuǎn)為 MapMap<String, Object> y3800PhoneMap = YamlUtils.getNestedMap(yamlMap, "Y3800", "phone");System.out.println("Y3800 -> phone : " + y3800NameMap);
}

具體來說,主要分為以下幾步:

  1. 首先判斷鍵路徑是否為空或者第一個(gè)鍵是否在 Map 中。如果鍵路徑為空或者第一個(gè)鍵不在 Map 中,則返回 null。
  2. 獲取第一個(gè)鍵對(duì)應(yīng)的嵌套對(duì)象。通過 get 方法獲取第一個(gè)鍵對(duì)應(yīng)的嵌套對(duì)象。
  3. 判斷是否到達(dá)最里層的嵌套 Map。如果鍵路徑長(zhǎng)度為 1,說明已經(jīng)到達(dá)最里層的嵌套 Map,直接返回該 Map 對(duì)象。
  4. 繼續(xù)遞歸查找下一個(gè)鍵的嵌套 Map。如果嵌套對(duì)象是 Map,則繼續(xù)遞歸查找下一個(gè)鍵的嵌套 Map。
  5. 返回結(jié)果。返回遞歸查找的結(jié)果。

對(duì)數(shù)據(jù)進(jìn)行脫敏處理

獲取到字段的脫敏規(guī)則后,我們就可以編寫方法實(shí)現(xiàn)對(duì)源數(shù)據(jù)做脫敏處理,脫敏方法如下:

/*** 使用指定規(guī)則對(duì)數(shù)據(jù)進(jìn)行脫敏處理** @param data 要進(jìn)行脫敏處理的數(shù)據(jù)* @param map 包含脫敏規(guī)則和格式的參數(shù)映射*            - "rule" 表示脫敏規(guī)則的正則表達(dá)式*            - "format" 表示替換脫敏部分的字符串,默認(rèn)為 "*"* @return 脫敏后的數(shù)據(jù)*/
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;
}

遞歸生成字段對(duì)應(yīng)的鍵路徑

目前我們已經(jīng)實(shí)現(xiàn)了通過字段的鍵路徑獲取到該字段對(duì)應(yīng)規(guī)則的方法 getNestedMapValues(),那么接下來我們只需要生成字段對(duì)應(yīng)的鍵路徑,然后調(diào)用方法 getNestedMapValues() 獲取到脫敏規(guī)則后調(diào)用 desensitizeLogic() 對(duì)源數(shù)據(jù)進(jìn)行脫敏即可。

提供源數(shù)據(jù)格式如下:

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

根據(jù)上述數(shù)據(jù)結(jié)構(gòu),首先我們需要從 txHeader 中獲取 servNo,之后遞歸遍歷 txEntity 中的元素即可。

具體方法如下:

/*** 對(duì)指定實(shí)體數(shù)據(jù)進(jìn)行脫敏處理** @param entity 要進(jìn)行脫敏處理的實(shí)體數(shù)據(jù)* @param servNo 當(dāng)前交易的服務(wù)號(hào),用于記錄日志* @param path 當(dāng)前實(shí)體數(shù)據(jù)在整個(gè)數(shù)據(jù)結(jié)構(gòu)中的路徑,用于記錄日志*/
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()) {// 計(jì)算當(dāng)前鍵值對(duì)在整個(gè)數(shù)據(jù)結(jié)構(gòu)中的路徑String currentPath = path.isEmpty() ? entry.getKey() : path + "," + entry.getKey();if (entry.getValue() instanceof Map) {// 如果當(dāng)前值是 Map 類型,則遞歸處理子節(jié)點(diǎn)parseData(entry.getValue(), servNo, currentPath);} else if (entry.getValue() instanceof List) {// 如果當(dāng)前值是 List 類型,則遍歷列表中的每個(gè)元素并遞歸處理子節(jié)點(diǎn)for (Object item : (List) entry.getValue()) {if (item instanceof Map) {parseData(item, servNo, currentPath);}}} else {// 如果當(dāng)前值不是 Map 或 List,則進(jìn)行脫敏處理String p = servNo + "," +currentPath;String[] keyPaths = p.split(",");// 獲取當(dāng)前節(jié)點(diǎn)的脫敏規(guī)則和格式Map<String, Object> nestedMap = getNestedMap(keyPaths);if(Objects.nonNull(nestedMap)){// 記錄日志log.info("-----------------交易【{}】,字段【{}】開始脫敏-----------------",servNo,currentPath.replace(",","->"));log.info("原始值:【{}:{}】",entry.getKey(),entry.getValue());log.info("脫敏規(guī)則:{}",nestedMap);// 對(duì)當(dāng)前節(jié)點(diǎn)的值進(jìn)行脫敏處理String desensitized = desensitizeLogic((String) entry.getValue(), nestedMap);entry.setValue(desensitized);// 記錄日志log.info("脫敏值:【{}:{}】",entry.getKey(),entry.getValue());log.info("-----------------交易【{}】,字段【{}】脫敏結(jié)束-----------------",servNo,currentPath.replace(",","->"));}}}}
}

該方法接收一個(gè)實(shí)體數(shù)據(jù) entity,一個(gè)服務(wù)號(hào) servNo 和一個(gè)路徑 path 作為參數(shù)。在方法體內(nèi),會(huì)遍歷實(shí)體數(shù)據(jù)的鍵值對(duì),并根據(jù)具體情況遞歸處理子節(jié)點(diǎn)或進(jìn)行脫敏處理。

  • 當(dāng)實(shí)體數(shù)據(jù)的值為 Map 類型時(shí),方法會(huì)遞歸處理子節(jié)點(diǎn);
  • 當(dāng)值為 List 類型時(shí),方法會(huì)遍歷列表中的每個(gè)元素并遞歸處理子節(jié)點(diǎn);
  • 當(dāng)值既不是 Map 也不是 List 時(shí),方法會(huì)根據(jù)服務(wù)號(hào)和路徑獲取脫敏規(guī)則,并對(duì)當(dāng)前節(jié)點(diǎn)的值進(jìn)行脫敏處理,并記錄脫敏日志。

脫敏處理的具體邏輯和規(guī)則通過調(diào)用 getNestedMap 方法和 desensitizeLogic 方法來實(shí)現(xiàn),其中 getNestedMap 方法用于獲取脫敏規(guī)則,desensitizeLogic 方法用于根據(jù)脫敏規(guī)則對(duì)數(shù)據(jù)進(jìn)行脫敏處理。

注:請(qǐng)注意本文中提供的數(shù)據(jù)樣例的層次結(jié)構(gòu)是和 YAML 中定義的結(jié)構(gòu)是一樣的,再通過上述方法遞歸后生成的鍵路徑是和從 YAML 中獲取規(guī)則所需的鍵路徑是一致的,因此可以直接調(diào)用 getNestedMapValues() 獲取脫敏規(guī)則。在實(shí)際使用中,其他數(shù)據(jù)結(jié)構(gòu)需要重寫該邏輯。

脫敏測(cè)試

編寫 Main 方法調(diào)用:

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();// 假設(shè)data中包含接口返回的數(shù)據(jù)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, "");}}}

運(yùn)行測(cè)試,控制臺(tái)輸出如下:

-----------------交易【Y3801】,字段【idCard】開始脫敏-----------------
原始值:【idCard:130428197001180384】
脫敏規(guī)則:{rule=(?<=\w{3})\w(?=\w{4}), format=+}
脫敏值:【idCard:130+++++++++++0384】
-----------------交易【Y3801】,字段【idCard】脫敏結(jié)束-----------------
-----------------交易【Y3801】,字段【list->phone】開始脫敏-----------------
原始值:【phone:17631007015】
脫敏規(guī)則:{rule=(\d{3})\d{4}(\d{4}), format=$1++++$2}
脫敏值:【phone:176++++7015】
-----------------交易【Y3801】,字段【list->phone】脫敏結(jié)束-----------------
-----------------交易【Y3801】,字段【list->phone】開始脫敏-----------------
原始值:【phone:17631007015】
脫敏規(guī)則:{rule=(\d{3})\d{4}(\d{4}), format=$1++++$2}
脫敏值:【phone:176++++7015】
-----------------交易【Y3801】,字段【list->phone】脫敏結(jié)束-----------------

數(shù)據(jù)脫敏后如下:

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

完整工具類

封裝成完整的工具類如下:

/*** @ClassName DataDesensitizationUtils* @Description 數(shù)據(jù)脫敏工具類* @Author 趙士杰* @Date 2024/1/25 20:15*/
@Slf4j
@SuppressWarnings("unchecked")
public class DataDesensitizationUtils {// YAML 文件路徑private static final String YAML_FILE_PATH = "/tuomin.yml";// 存儲(chǔ)解析后的 YAML 數(shù)據(jù)private static Map<String, Object> map;static {// 創(chuàng)建 Yaml 對(duì)象Yaml yaml = new Yaml();// 通過 getResourceAsStream 獲取 YAML 文件的輸入流try (InputStream in = DataDesensitizationUtils.class.getResourceAsStream(YAML_FILE_PATH)) {// 解析 YAML 文件為 Map 對(duì)象map = yaml.loadAs(in, Map.class);} catch (Exception e) {e.printStackTrace();}}/*** 獲取嵌套的 Map 數(shù)據(jù)** @param keys 嵌套鍵路徑* @return 嵌套數(shù)據(jù)對(duì)應(yīng)的 Map*/private static Map<String, Object> getNestedMap(String... keys) {return getNestedMapValues(map, keys);}/*** 遞歸獲取嵌套 Map 數(shù)據(jù)** @param map  嵌套數(shù)據(jù)源的 Map* @param keys 嵌套鍵路徑* @return 嵌套數(shù)據(jù)對(duì)應(yīng)的 Map*/private static Map<String, Object> getNestedMapValues(Map<String, Object> map, String... keys) {// 如果鍵路徑為空或者第一個(gè)鍵不在 Map 中,則返回 nullif (keys.length == 0 || !map.containsKey(keys[0])) {return null;}// 獲取第一個(gè)鍵對(duì)應(yīng)的嵌套對(duì)象Object nestedObject = map.get(keys[0]);// 如果鍵路徑長(zhǎng)度為 1,說明已經(jīng)到達(dá)最里層的嵌套 Map,直接返回該 Map 對(duì)象if (keys.length == 1) {if (nestedObject instanceof Map) {return (Map<String, Object>) nestedObject;} else {return null;}} else {// 如果嵌套對(duì)象是 Map,繼續(xù)遞歸查找下一個(gè)鍵的嵌套 Mapif (nestedObject instanceof Map) {return getNestedMapValues((Map<String, Object>) nestedObject, Arrays.copyOfRange(keys, 1, keys.length));} else {// 嵌套對(duì)象既不是 Map 也不是 List,返回 nullreturn null;}}}/*** 對(duì)指定實(shí)體數(shù)據(jù)進(jìn)行脫敏處理** @param entity 要進(jìn)行脫敏處理的實(shí)體數(shù)據(jù)* @param servNo 當(dāng)前交易的服務(wù)號(hào),用于記錄日志* @param path 當(dāng)前實(shí)體數(shù)據(jù)在整個(gè)數(shù)據(jù)結(jié)構(gòu)中的路徑,用于記錄日志*/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("脫敏規(guī)則:{}", nestedMap);String desensitized = desensitizeLogic((String) entry.getValue(), nestedMap);entry.setValue(desensitized);log.info("脫敏值:【{}:{}】", entry.getKey(), entry.getValue());log.info("-----------------交易【{}】,字段【{}】脫敏結(jié)束-----------------", servNo, currentPath.replace(",", "->"));}}}}}/*** 脫敏邏輯* @param data 源數(shù)據(jù)* @param map 脫敏規(guī)則* @return 脫敏后的數(shù)據(jù)*/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;}}
http://www.risenshineclean.com/news/54808.html

相關(guān)文章:

  • 如何實(shí)現(xiàn)網(wǎng)站開發(fā)太原網(wǎng)站建設(shè)制作
  • 廣州建筑集團(tuán)股份有限公司杭州seo排名優(yōu)化外包
  • 如何提取網(wǎng)頁中的視頻seo主要做什么
  • 龍崗區(qū)住房建設(shè)局網(wǎng)站品牌營(yíng)銷方案
  • 云盤做網(wǎng)站關(guān)鍵詞推廣營(yíng)銷
  • 廈門網(wǎng)站建設(shè)哪家好優(yōu)化網(wǎng)絡(luò)的軟件
  • 企業(yè)網(wǎng)站建設(shè)webbj免費(fèi)網(wǎng)站優(yōu)化排名
  • 湛江疫情最新通報(bào)五年級(jí)上冊(cè)語文優(yōu)化設(shè)計(jì)答案
  • 什么是網(wǎng)站解決方案武漢網(wǎng)絡(luò)推廣有哪些公司
  • 網(wǎng)站留言評(píng)論功能深圳百度seo代理
  • 網(wǎng)站建設(shè)合同印花稅稅目外鏈?zhǔn)珍浘W(wǎng)站
  • vs2015網(wǎng)站開發(fā)教程seo搜索優(yōu)化待遇
  • 青島做網(wǎng)站的公司深圳市前十的互聯(lián)網(wǎng)推廣公司
  • asp網(wǎng)站后臺(tái)安全退出購物網(wǎng)站
  • 做網(wǎng)站哪家南京做網(wǎng)站網(wǎng)站關(guān)鍵詞挖掘
  • 網(wǎng)站建設(shè)市場(chǎng)前景體育新聞最新消息
  • 網(wǎng)站沒有被搜索引擎收錄東莞seo排名公司
  • 愛情動(dòng)做網(wǎng)站推薦收錄批量查詢
  • 國內(nèi)做賭博網(wǎng)站代理怎么樣加快百度收錄的方法
  • 分布式移動(dòng)網(wǎng)站開發(fā)技術(shù)一個(gè)品牌的策劃方案
  • 南昌哪里可以做電商網(wǎng)站seo收索引擎優(yōu)化
  • seo策略是什么青島seo推廣
  • 網(wǎng)站開發(fā)方向行業(yè)現(xiàn)狀青島網(wǎng)站建設(shè)制作公司
  • 建網(wǎng)站報(bào)價(jià) 優(yōu)幫云web免費(fèi)網(wǎng)站
  • 常州做沙灘旗的公司網(wǎng)站做網(wǎng)絡(luò)優(yōu)化的公司排名
  • 安裝網(wǎng)站系統(tǒng)個(gè)人網(wǎng)絡(luò)銷售平臺(tái)
  • 建設(shè)獨(dú)立服務(wù)器網(wǎng)站成人技能培訓(xùn)
  • 用discuz做的網(wǎng)站代哥seo
  • 中山市飲食網(wǎng)站建設(shè)網(wǎng)站性能優(yōu)化
  • 外貿(mào)行業(yè)建站廣告代運(yùn)營(yíng)公司