資料脫敏

curry库-04049發表於2024-11-06

目錄
  • 一.Hutool庫
  • 二.配合JackSon透過註解方式實現脫敏

根據搜尋結果,MyBatis-Flex、Hutool庫和Jackson是三種常用的脫敏方式,它們各有特點和應用場景。以下是它們的比較:

  1. MyBatis-Flex

    • 提供了@ColumnMask()註解,並內建了9種脫敏規則,如手機號、固定電話、身份證號、車牌號、地址、郵件、密碼和銀行卡號脫敏。
    • 支援自定義脫敏規則,透過MaskManager註冊新的脫敏規則。
    • 適用於ORM場景,特別是在資料訪問層對查詢結果進行脫敏處理。
  2. Hutool庫

    • 提供了DesensitizedUtil工具類,支援多種脫敏資料型別,包括使用者id、中文姓名、身份證號、座機號、手機號、地址、電子郵件、密碼、中國大陸車牌和銀行卡。
    • 透過靜態方法一行程式碼實現脫敏,如DesensitizedUtil.mobilePhone()DesensitizedUtil.bankCard()等。
    • 適用於需要快速、靈活進行資料脫敏的場景,特別是在Java程式碼中直接處理資料脫敏。
  3. Jackson

    • 透過自定義序列化器和註解方式實現脫敏,可以在JSON序列化時進行資料脫敏。
    • 支援自定義脫敏策略,透過定義策略類和工廠類來實現不同的脫敏邏輯。
    • 適用於Web應用中,特別是在物件轉換為JSON響應體時對敏感資料進行脫敏。

綜合來看,Hutool庫因其簡單易用和廣泛的脫敏資料型別支援,可能是最常用的脫敏方式。它提供了一行程式碼就能完成脫敏的便捷方法,適用於多種常見的敏感資訊脫敏場景。而MyBatis-Flex和Jackson則更多地用於特定的ORM和JSON序列化場景。因此,如果要選擇最常用的脫敏方式,Hutool庫可能是首選。

基於SpringBoot

一.Hutool庫

  • 配置Maven依賴
<!-- Hutool工具類庫 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.16</version> <!-- 請使用最新的版本號 -->
        </dependency>
  • 建立一個測試類
package com.curry.desensitization;

import cn.hutool.core.util.DesensitizedUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:00
 * @Description:hutool脫敏工具測試類
 */
@SpringBootTest //啟動測試類
public class HuToolDesensitizationTest {
    /**
     * 測試手機號脫敏
     */

    @Test
    public void testPhoneDesensitization() {
        String phone="13723231234";
        System.out.println(DesensitizedUtil.mobilePhone(phone));
        //輸出:137****1234
    }
    /**
     * 測試銀行卡號脫敏
     */

    @Test
    public void testBankCardDesensitization() {
        String bankCard="6217000130008255666";
        System.out.println(DesensitizedUtil.bankCard(bankCard)); 
        //輸出:6217 **** **** *** 5666
    }

    /**
     * 測試身份證號脫敏
     */

    @Test
    public void testIdCardNumDesensitization() {
        String idCardNum="411021199901102321";
        //只顯示前4位和後2位
        System.out.println(DesensitizedUtil.idCardNum(idCardNum,4,2)); 
        //輸出:4110************21
    }

    /**
     * 測試郵箱脫敏
     */
    @Test
    public void testPasswordDesensitization() {
        String password="www.jd.com_35711";
        System.out.println(DesensitizedUtil.password(password)); 
        //輸出:****************
    }
}

執行結果為:

二.配合JackSon透過註解方式實現脫敏

專案是基於 Spring Boot 的 web 專案,則可以利用 Spring Boot 自帶的 jackson 自定義序列化實現。它的實現原理其實就是在 json 進行序列化渲染給前端時,進行脫敏

第一步:脫敏策略的列舉(建立一個util包,在util包下建立DesensitizationTypeEnum列舉類)

package com.curry.util;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:06
 * @Description:脫敏策略列舉
 */
public enum DesensitizationTypeEnum {
    //自定義
    MY_RULE,
    //使用者id
    USER_ID,
    //中文名
    CHINESE_NAME,
    //身份證號
    ID_CARD,
    //座機號
    FIXED_PHONE,
    //手機號
    MOBILE_PHONE,
    //地址
    ADDRESS,
    //電子郵件
    EMAIL,
    //密碼
    PASSWORD,
    //中國大陸車牌,包含普通車輛、新能源車輛
    CAR_LICENSE,
    //銀行卡
    BANK_CARD
}

第二步:定義一個用於脫敏的 Desensitization 註解

@Retention (RetentionPolicy.RUNTIME):執行時生效。

@Target (ElementType.FIELD):可用在欄位上。

@JacksonAnnotationsInside:此註解可以點進去看一下是一個元註解,主要是使用者打包其他註解一起使用。

@JsonSerialize:上面說到過,該註解的作用就是可自定義序列化,可以用在註解上,方法上,欄位上,類上,執行時生效等等,根據提供的序列化類裡面的重寫方法實現自定義序列化。

package com.curry.desensitization;

import com.curry.util.DesensitizationTypeEnum;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:08
 * @Description:desensitization註解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
    /**
     * 脫敏資料型別,在MY_RULE的時候,startInclude和endExclude生效
     */
    DesensitizationTypeEnum type() default DesensitizationTypeEnum.MY_RULE;

    /**
     * 脫敏開始位置(包含)
     */
    int startInclude() default 0;

    /**
     * 脫敏結束位置(不包含)
     */
    int endExclude() default 0;
}

注:只有使用了自定義的脫敏列舉 MY_RULE 的時候,開始位置和結束位置才生效

第三步:建立自定的序列化類

這一步是我們實現資料脫敏的關鍵。自定義序列化類繼承 JsonSerializer,實現 ContextualSerializer 介面,並重寫兩個方法

新建DesensitizationSerialize類

package com.curry.desensitization;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:10
 * @Description:
 */

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.curry.util.DesensitizationTypeEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.util.Objects;

/**
 * @author
 * @description: 自定義序列化類
 */
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
    private DesensitizationTypeEnum type;

    private Integer startInclude;

    private Integer endExclude;

    @Override
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws
            IOException {
        switch (type) {
            // 自定義型別脫敏
            case MY_RULE:
                jsonGenerator.writeString(CharSequenceUtil.hide(str, startInclude, endExclude));
                break;
            // userId脫敏
            case USER_ID:
                jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId()));
                break;
            // 中文姓名脫敏
            case CHINESE_NAME:
                jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str)));
                break;
            // 身份證脫敏
            case ID_CARD:
                jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 1, 2));
                break;
            // 固定電話脫敏
            case FIXED_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
                break;
            // 手機號脫敏
            case MOBILE_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
                break;
            // 地址脫敏
            case ADDRESS:
                jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
                break;
            // 郵箱脫敏
            case EMAIL:
                jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
                break;
            // 密碼脫敏
            case PASSWORD:
                jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
                break;
            // 中國車牌脫敏
            case CAR_LICENSE:
                jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
                break;
            // 銀行卡脫敏
            case BANK_CARD:
                jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
                break;
            default:
        }

    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws
            JsonMappingException {
        if (beanProperty != null) {
            // 判斷資料型別是否為String型別
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                // 獲取定義的註解
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                // 為null
                if (desensitization == null) {
                    desensitization = beanProperty.getContextAnnotation(Desensitization.class);
                }
                // 不為null
                if (desensitization != null) {
                    // 建立定義的序列化類的例項並且返回,入參為註解定義的type,開始位置,結束位置。
                    return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
                            desensitization.endExclude());
                }
            }

            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }
}

首先定義一個要測試的 pojo,對應的欄位加入要脫敏的策略

package com.curry.Entity;

import com.curry.desensitization.Desensitization;
import com.curry.util.DesensitizationTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:16
 * @Description:測試pojo類
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestPojo {

    private String userName;

    @Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
    private String phone;

    @Desensitization(type = DesensitizationTypeEnum.PASSWORD)
    private String password;

    @Desensitization(type = DesensitizationTypeEnum.MY_RULE, startInclude = 0, endExclude = 2)
    private String address;
}

寫一個測試的controller

package com.curry.controller;

import com.curry.Entity.TestPojo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 藍影
 * @Date: 2024/11/5 19:15
 * @Description:controller測試類
 */
@RestController
public class TestController {

    @PostMapping("/test")
    public TestPojo testDesensitization(){
        TestPojo testPojo = new TestPojo();
        testPojo.setUserName("我是使用者名稱");
        testPojo.setAddress("地球中國-北京市通州區京東總部2號樓");
        testPojo.setPhone("13782946666");
        testPojo.setPassword("sunyangwei123123123.");
        System.out.println(testPojo);
        return testPojo;
    }

}

在postman測試結果為

相關文章