SpringBoot--数据脱敏

2,679 阅读3分钟
  • 基础脱敏字段
/**
 * @date 2020/9/29 16:22
 * 脱敏字段:所有需要脱敏的字段名称
 */
public class BaseField implements Serializable {

    private static final long serialVersionUID = 1L;
    /**
     * 中文名称
     */
    @DesensitizedAnnotation(type = TypeEnum.CHINESE_NAME)
    private String username;

    /**
     * 手机号码
     */
    @DesensitizedAnnotation(type = TypeEnum.MOBILE_PHONE)
    private String phone;

    /**
     * 密码
     */
    @DesensitizedAnnotation(type = TypeEnum.PASSWORD)
    private String password;

    /**
     * 地址
     */
    @DesensitizedAnnotation(type = TypeEnum.ADDRESS)
    private String address;

    /**
     * 银行卡号
     */
    @DesensitizedAnnotation(type = TypeEnum.BANKCARD)
    private String bankCard;

    /**
     * 邮箱地址
     */
    @DesensitizedAnnotation(type = TypeEnum.EMAIL)
    private String email;

    /**
     * 固定电话号码
     */
    @DesensitizedAnnotation(type = TypeEnum.FIXED_PHONE)
    private String fixedPhone;

    /**
     * 证件号
     */
    @DesensitizedAnnotation(type = TypeEnum.ID_CARD_NUM)
    private String idCardNum;
}
  • 脱敏注解
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DesensitizedAnnotation {
    /**
     * 脱敏数据类型(规则)
     */
    TypeEnum type();
}
  • 枚举类
public enum TypeEnum {
    /**
     * 名称
     **/
    CHINESE_NAME,
    /**
     * 身份证证件号
     **/
    ID_CARD_NUM,
    /**
     * 手机号
     **/
    MOBILE_PHONE,
    /**
     * 固定电话
     */
    FIXED_PHONE,
    /**
     * 密码
     **/
    PASSWORD,
    /**
     * 银行卡号
     */
    BANKCARD,
    /**
     * 邮箱
     */
    EMAIL,
    /**
     * 地址
     */
    ADDRESS,
}
  • 脱敏工具类

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @date 2020/9/29 16:23
 */
public class DesensitizedUtils {

    private static final Logger logger = LoggerFactory.getLogger(DesensitizedUtils.class);

    /**
     * 存储所有需要脱敏的字段
     */
    private static final Map<String, TypeEnum> annotationMaps = new HashMap<>();

    /**
     * 类加载时装配待脱敏字段
     */
    static {
        try {
            Class<?> clazz = Class.forName(BaseField.class.getName());
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                DesensitizedAnnotation annotation = fields[i].getAnnotation(DesensitizedAnnotation.class);
                if (annotation != null) {
                    TypeEnum type = annotation.type();
                    String name = fields[i].getName();
                    //name为注解字段名称,value为注解类型。方便后续根据注解类型扩展
                    DesensitizedUtils.annotationMaps.put(name, type);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            DesensitizedUtils.logger.error("类加载时装配待脱敏字段异常,异常信息:[{}]", new Object[]{e});
        }
    }


    /**
     * 脱敏:可以实现Map或者JavaBean两种模式脱敏
     *
     * @param object 需要脱敏的对象
     */
    public static String desensitization(Object object) {
        String objClassName = object.getClass().getName();
        try {
            // 1.处理Map数据类型
            if (object instanceof Map) {
                HashMap<String, Object> reqMap = (HashMap) object;
                Iterator<String> iterator = DesensitizedUtils.annotationMaps.keySet().iterator();
                iterator.forEachRemaining(annotationName -> {
                    if (reqMap.keySet().contains(annotationName)) {
                        DesensitizedUtils.doconverentForMap(reqMap, annotationName);
                    }
                });
                return JSON.toJSONString(reqMap);
            }
            // 2.处理Object数据类型
            Object val = new Object();
            Class<?> objClazz = Class.forName(objClassName);
            Field[] declaredFields = objClazz.getDeclaredFields();
            for (int j = 0; j < declaredFields.length; j++) {
                Iterator<String> iterator = DesensitizedUtils.annotationMaps.keySet().iterator();
                while (iterator.hasNext()) {
                    String annotationName = iterator.next();
                    if (declaredFields[j].getName().equals(annotationName)) {
                        declaredFields[j].setAccessible(true);
                        val = declaredFields[j].get(object);
                        //获取属性后现在默认处理的是String类型,其他类型数据可扩展
                        String value = DesensitizedUtils.doconverentForObject(val, annotationName);
                        declaredFields[j].set(object, value);
                    }
                }
            }
            return JSON.toJSONString(object);
        } catch (Exception e) {
            e.printStackTrace();
            DesensitizedUtils.logger.error("日志脱敏处理失败,回滚,详细信息:[{}]", new Object[]{e});
            return JSON.toJSONString(object);
        }
    }

    /**
     * 脱敏数据源为Object(JavaBean)时处理方式
     */
    private static String doconverentForObject(Object val, String annotationName) {

        String value = String.valueOf(val);
        if (StringUtils.isNotEmpty(value)) {
            value = DesensitizedUtils.doConverentByType(value, annotationName);
        }
        return value;
    }

    /**
     * 脱敏数据源为Map时处理方式
     */
    private static void doconverentForMap(HashMap<String, Object> reqMap, String annotationName) {
        String value = String.valueOf(reqMap.get(annotationName));
        if (StringUtils.isNotEmpty(value)) {
            value = DesensitizedUtils.doConverentByType(value, annotationName);
        }
        reqMap.put(annotationName, value);
    }


    /**
     * 根据不同注解类型处理不同字段
     */
    private static String doConverentByType(String value, String annotationName) {
        TypeEnum typeEnum = DesensitizedUtils.annotationMaps.get(annotationName);
        switch (typeEnum) {
            case CHINESE_NAME:
                value = DesensitizedUtils.chineseName(value);
                break;
            case ID_CARD_NUM:
                value = DesensitizedUtils.idCardNum(value);
                break;
            case MOBILE_PHONE:
                value = DesensitizedUtils.mobilePhone(value);
                break;
            case FIXED_PHONE:
                value = DesensitizedUtils.fixedPhone(value);
                break;
            case PASSWORD:
                value = DesensitizedUtils.password(value);
                break;
            case BANKCARD:
                value = DesensitizedUtils.bankCard(value);
                break;
            case EMAIL:
                value = DesensitizedUtils.email(value);
                break;
            case ADDRESS:
                value = DesensitizedUtils.address(value);
                break;
        }
        return value;
    }

    /*--------------------------下面的脱敏工具类也可以单独对某一个字段进行使用-------------------------*/

    /**
     * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
     */
    public static String chineseName(String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     * 【身份证号】显示最后四位,其他隐藏。共计18位或者15位,比如:*************1234
     */
    public static String idCardNum(String id) {
        if (StringUtils.isBlank(id)) {
            return "";
        }
        String num = StringUtils.right(id, 4);
        return StringUtils.leftPad(num, StringUtils.length(id), "*");
    }

    /**
     * 【固定电话】 显示后四位,其他隐藏,比如:*******3241
     */
    public static String fixedPhone(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
    }

    /**
     * 【手机号码】前三位,后四位,其他隐藏,比如:135****6810
     */
    public static String mobilePhone(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
    }

    /**
     * 【地址】只显示到地区,不显示详细地址,比如:湖南省长沙市岳麓区***
     */
    public static String address(String address) {
        int sensitiveSize = 8;
        if (StringUtils.isBlank(address)) {
            return "";
        }
        int length = StringUtils.length(address);
        return StringUtils.rightPad(StringUtils.left(address, (length - sensitiveSize) < 0 ? 0 : length - sensitiveSize), length, "*");
    }

    /**
     * 【电子邮箱】 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
     */
    public static String email(String email) {
        if (StringUtils.isBlank(email)) {
            return "";
        }
        int index = StringUtils.indexOf(email, "@");
        if (index <= 1) {
            return email;
        } else {
            return StringUtils.rightPad(StringUtils.left(email, 1), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email)));
        }
    }

    /**
     * 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:6222600**********1234
     */
    public static String bankCard(String cardNum) {
        if (StringUtils.isBlank(cardNum)) {
            return "";
        }
        return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******"));
    }

    /**
     * 【密码】密码的全部字符都用*代替,比如:******
     */
    public static String password(String password) {
        if (StringUtils.isBlank(password)) {
            return "";
        }
        String pwd = StringUtils.left(password, 0);
        return StringUtils.rightPad(pwd, StringUtils.length(password), "*");
    }
}
  • 测试

person

==注意:== 业务实体需要脱敏的字段要和BaseFiled类中字段一样

@Data
public class Person implements Serializable {
    private String id;

    private String username;

    private String password;

    private String phone;

    private String address;
}

TestController

@RestController
public class TestController {

@PostMapping("hello")
public Person test() {
    Person person = new Person();
    person.setId("1");
    person.setUsername("小张");
    person.setPassword("123456");
    person.setPhone("18888888888");
    person.setAddress("湖南省长沙市岳麓区麓谷企业广场");
    System.out.println("脱敏前:" + person);
    DesensitizedUtils.desensitization(person);

    HashMap<String, Object> map = new HashMap<>(16);
    map.put("username", "小张");
    map.put("password", "123456789");
    map.put("phone", "18888888888");
    map.put("address", "湖南省长沙市岳麓区麓谷企业广场");
    DesensitizedUtils.desensitization(map);
    return person;
}
}