6 Star 72 Fork 28

JustryDeng / notebook

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
[10]Jackson数据脱敏.md 6.16 KB
一键复制 编辑 原始数据 按行查看 历史
邓沙利文 提交于 2022-10-19 11:26 . Jackson序列化基础配置

Jackson数据脱敏

Jackson数据脱敏

第一步:先定义一个脱敏策略类

import lombok.Getter;


/**
 *  脱敏策略
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Getter
public enum DefenderStrategy {
    
    /** 姓名类脱敏策略 */
    NAME(1, 1, '*'),
    
    /** 账号类脱敏策略 */
    ACCOUNT_NO(2, 2, '*'),
    
    /** 邮箱类脱敏策略 */
    EMAIL(2, 7, '*'),
    
    /** 身份证号类脱敏策略 */
    ID_CARD(6, 4, '*'),
    
    /** 手机号码类脱敏策略 */
    PHONE_NUMBER(3, 4, '*'),
    
    /** 住址类脱敏策略 */
    ADDRESS(3, 4, '*');
    
    
    private final int retainPrefixCount;
    
    private final int retainSuffixCount;
    
    private final char replaceChar;
    
    DefenderStrategy(int retainPrefixCount, int retainSuffixCount, char replaceChar) {
        this.retainPrefixCount = retainPrefixCount;
        this.retainSuffixCount = retainSuffixCount;
        this.replaceChar = replaceChar;
    }
}

第二步:编写脱敏序列化器

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.Getter;

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

/**
 * 脱敏序列化器
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Getter
public class DesensitizeSerializer extends JsonSerializer<String> implements ContextualSerializer {
    
    protected DefenderStrategy strategy;
    
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (this.strategy != null) {
            gen.writeString(doReplace(value, this.strategy));
        } else {
            gen.writeString(value);
        }
    }
    
    /**
     * 获取属性上的注解属性
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Desensitize annotation = property.getAnnotation(Desensitize.class);
        if (Objects.nonNull(annotation)&&Objects.equals(String.class, property.getType().getRawClass())) {
            this.strategy = annotation.value();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
        
    }
    
    
    /**
     * 替换(即:脱敏)
     *
     * @param str
     *         要脱敏的字符串
     * @param strategy
     *         脱敏策略
     * @return 脱敏后的字符串
     */
    public static String doReplace(String str, DefenderStrategy strategy) {
        // 脱敏起始位置索引
        int startIndex = strategy.getRetainPrefixCount();
        if (str == null || str.length() <= startIndex) {
            return str;
        }
        int suffixCount = strategy.getRetainSuffixCount();
        int diffLength = str.length() - startIndex - suffixCount;
        // 脱敏结束位置索引
        int endIndex = diffLength <= 0 ? str.length() : str.length() - suffixCount;
        char replaceChar = strategy.getReplaceChar();
        String replacerStr = getReplacerString(replaceChar, endIndex - startIndex);
        return new StringBuilder(str).replace(startIndex, endIndex, replacerStr).toString();
    }
    
    /**
     * 拼接length个replacer
     *
     * @param replacer
     *         要拼接的字符
     * @param length
     *         要拼接的个数
     * @return 拼接后的字符串
     */
    public static String getReplacerString(char replacer, int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(replacer);
        }
        return sb.toString();
    }
}

第三步:定义标识注解

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 <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@JacksonAnnotationsInside
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JsonSerialize(using = DesensitizeSerializer.class)
public @interface Desensitize {
    
    /**
     * 脱敏策略
     */
    DefenderStrategy value();
}

测试一下

  • 第一步:在响应类要脱敏的字段上使用脱敏注解

    @Desensitize(DefenderStrategy.NAME)
    private String name;
    
    @Desensitize(DefenderStrategy.ID_CARD)
    private String idNumber;
  • 第二步:访问接口,观察数据是否脱敏

    {
        "data": {
            "total": 0,
            "pageNum": 1,
            "pageSize": 10,
            "dataList": [
                {
                    "name": "张*",
                    "idNumber": "5****************1"
                },
                {
                    "name": "李*****n",
                    "idNumber": "5****************8",
                }
            ]
        },
        "code": 200,
        "msg": "成功",
        "trace_id": "8bde6d50df7c4be499fe2bb4b6cfdf0c"
    }
1
https://gitee.com/JustryDeng/notebook.git
git@gitee.com:JustryDeng/notebook.git
JustryDeng
notebook
notebook
master

搜索帮助