基于注解形式的数据脱敏

Posted bevis-byf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于注解形式的数据脱敏相关的知识,希望对你有一定的参考价值。

数据脱敏

注解定义

package cn.com.sensitive.annotations;

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

/**
 * 敏感方法
 *
 * @author zhanghao
 * @date 2019/06/04
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Sensitive {
}
package cn.com.sensitive.annotations;

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

/**
 * 数据脱敏
 * 证件类型标识
 *
 * @author zhanghao
 * @date 2019/06/04
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface SensitiveCardType {

    String idCardType() default "111";

    String refField() default "";
}
package cn.com.sensitive.annotations;

import cn.com.sensitive.enums.SensitiveType;

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

/**
 * 数据脱敏
 * 敏感字段
 *
 * @author zhanghao
 * @date 2019/06/04
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface SensitiveField {

    SensitiveType value();

    SensitiveCardType cardRef() default @SensitiveCardType();
}

 

枚举类定义

package cn.com.sensitive.enums;

/**
 * 敏感数据类型
 *
 * @author zhanghao
 * @date 2019/06/04
 */
public enum SensitiveType {
    /**
     * 姓名
     */
    NAME,
    /**
     * 手机
     */
    MOBILE,
    /**
     * 邮箱
     */
    EMAIL,
    /**
     * 身份证
     */
    IDCARD,
    /**
     * 生日
     */
    BIRTHDAY,
    /**
     * 密码
     */
    PASSWORD,
    /**
     * 银行卡
     */
    BANKCARD,
    /**
     * 薪水
     */
    SALARY,
    /**
     * 其他证件号码
     */
    OTHERCARD,
    /**
     * 不确定哪种证件类型,指向某个字段确定类型
     */
    CARD_REF,
    /**
     * 对象(将对标记为OBJECT的对象会处理该对象里面的字段)
     */
    OBJECT


}

脱敏工具类定义

package cn.com.sensitive.utils;

import cn.com.sensitive.SensitiveHandler;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

/**
 * 数据隐私显示 手机号,身份证号和银行卡号等
 */
public class SensitiveUtils {

    /**
     * 手机号
     * 手机号留前4位和后4位,其他*
     *
     * @param phone
     * @return
     */
    public static String maskMobile(String phone) {
        if(StringUtils.isBlank(phone)) {
            return "";
        }
        if(StringUtils.length(phone)<8) {
            return phone;
        }
        int start = 4;
        int end = phone.length() - 4;
        StringBuffer overPlay = new StringBuffer();
        for (int i = start; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(phone, overPlay.toString(), start, end);
    }

    /**
     * 邮箱账号
     * 邮箱留前4位及@符号以后,其他*
     *
     * @param email
     * @return
     */
    public static String maskEmail(String email) {
        if (StringUtils.isEmpty(email)) {
            return "";
        }
        String at = "@";
        if (!email.contains(at)) {
            return email;
        }
        /**
         * 这里主要逻辑是需要保留邮箱的注册商 比如@qq.com
         * 后四位打码,不足四位,除了@前都打码
         */
        int index = StringUtils.indexOf(email, at);
        String content = StringUtils.substring(email, 0, index);
        String mask = "";
        if (content.length() > 4) {
            int start = 4;
            int end = index;
            StringBuffer overPlay = new StringBuffer();
            for (int i = start; i < end; i++) {
                overPlay.append("*");
            }
            mask = StringUtils.overlay(content, overPlay.toString(), 4, content.length());
        } else {
            int start = 0;
            int end = index;
            StringBuffer overPlay = new StringBuffer();
            for (int i = start; i < end; i++) {
                overPlay.append("*");
            }
            mask = overPlay.toString();
        }
        return mask + StringUtils.substring(email, index);
    }

    /**
     * 身份证打码操作
     * 身份证留前4位和后3位,其他 *
     *
     * @param idCard
     * @return
     */
    public static String maskIdCard(String idCard) {
        if(StringUtils.isBlank(idCard)) {
            return "";
        }
        if(StringUtils.length(idCard)<7) {
            return idCard;
        }
        int start = 4;
        int end = idCard.length() - 3;
        StringBuffer overPlay = new StringBuffer();
        for (int i = start; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(idCard, overPlay.toString(), start, end);
    }

    /**
     * 生日打码操作
     * 中间月日打码。生日年月日
     *
     * @param birthday
     * @return
     */
    public static String maskBirthday(String birthday) {
        if(StringUtils.isBlank(birthday)) {
            return "";
        }
        if(StringUtils.length(birthday)<=4) {
            return birthday;
        }
        String pre = birthday.substring(0, 4);
        String suf = birthday.substring(4);
        String sufResult = StringUtils.replaceAll(suf, "[0-9]", "*");
        return pre + sufResult;
    }

    /**
     * 银行卡号
     * 银行账号留前4位和后4位,其他*
     *
     * @param bandCard
     * @return
     */
    public static String maskBankCard(String bandCard) {
        if(StringUtils.isBlank(bandCard)) {
            return "";
        }
        if(StringUtils.length(bandCard)<8) {
            return bandCard;
        }
        int start = 4;
        int end = bandCard.length() - 4;
        StringBuffer overPlay = new StringBuffer();
        for (int i = start; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(bandCard, overPlay.toString(), start, end);
    }

    /**
     * 密码全部打码
     *
     * @param password
     * @return
     */
    public static String maskPassword(String password) {
        if(StringUtils.isBlank(password)) {
            return "";
        }
        int end = password.length();
        StringBuffer overPlay = new StringBuffer();
        for (int i = 0; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(password, overPlay.toString(), 0, end);
    }

    /**
     * 中文姓名,除了第一位不打码
     *
     * @param name
     * @return
     */
    public static String maskName(String name) {
        if(StringUtils.isBlank(name)) {
            return "";
        }
        int end = name.length();
        StringBuffer overPlay = new StringBuffer();
        for (int i = 1; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(name, overPlay.toString(), 1, end);
    }

    /**
     * 月薪,全部*
     *
     * @param salary
     * @return
     */
    public static String maskSalary(String salary) {
        if(StringUtils.isBlank(salary)) {
            return "";
        }
        int end = salary.length();
        StringBuffer overPlay = new StringBuffer();
        for (int i = 0; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(salary, overPlay.toString(), 0, end);
    }

    /**
     * 其他证件号码前1位和后3位其他全部为*
     *
     * @param otherCard
     * @return
     */
    public static String maskOtherCard(String otherCard) {
        if(StringUtils.isBlank(otherCard)) {
            return "";
        }
        if(StringUtils.length(otherCard)<4) {
            return otherCard;
        }
        int start = 1;
        int end = otherCard.length() - 3;
        StringBuffer overPlay = new StringBuffer();
        for (int i = start; i < end; i++) {
            overPlay.append("*");
        }
        return StringUtils.overlay(otherCard, overPlay.toString(), start, end);
    }


    /**
     * 对list结果集支持
     * @param list
     * @param <T>
     * @throws Exception
     */
    public static <T> void supportList(List<T> list){
        for (T t : list) {
            try {
                SensitiveHandler.handle(t);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 对object结果集支持
     * @param t
     * @param <T>
     * @throws Exception
     */
    public static <T> void supportObject(T t){
        try {
            SensitiveHandler.handle(t);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

 

核心处理器

package cn.com.sensitive;

import cn.com.sensitive.annotations.SensitiveCardType;
import cn.com.sensitive.annotations.SensitiveField;
import cn.com.sensitive.enums.SensitiveType;
import cn.com.sensitive.utils.SensitiveUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;

public class SensitiveHandler {

    public static void handle(Object obj) throws Exception {
        if (null != obj) {
            Class<?> objClazz = obj.getClass();
            if (null != objClazz) {
                List<Field> allFieldsList = FieldUtils.getAllFieldsList(objClazz);
                if (CollectionUtils.isNotEmpty(allFieldsList)) {
                    for (Field declaredField : allFieldsList) {
                        declaredField.setAccessible(true);
                        SensitiveField sensitiveField = declaredField.getAnnotation(SensitiveField.class);
                        if (null != sensitiveField) {
                            Object fieldVal = declaredField.get(obj);
                            if (null != fieldVal) {
                                if (SensitiveType.OBJECT.equals(sensitiveField.value())) {
                                    handle(fieldVal);
                                } else if(SensitiveType.CARD_REF.equals(sensitiveField.value())) {
                                    // 处理
                                    try {
                                        SensitiveCardType sensitiveCardType = sensitiveField.cardRef();
                                        if(Objects.nonNull(sensitiveCardType)) {
                                            String idCardType = sensitiveCardType.idCardType();
                                            String refField = sensitiveCardType.refField();
                                            if(StringUtils.isNoneBlank(idCardType,refField)) {
                                                for (Field declaredFieldCur : allFieldsList) {
                                                    declaredFieldCur.setAccessible(Boolean.TRUE);
                                                    if(declaredFieldCur.getName().equals(refField)) {
                                                        Object idCardTypeVal = declaredFieldCur.get(obj);
                                                        String valStr = (String) fieldVal;
                                                        if(String.valueOf(idCardTypeVal).equals(idCardType)) {
                                                            String result = handleSensitiveString(SensitiveType.IDCARD, valStr);
                                                            declaredField.set(obj, result);
                                                        } else {
                                                            String result = handleSensitiveString(SensitiveType.OTHERCARD, valStr);
                                                            declaredField.set(obj, result);
                                                        }
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                } else {
                                    // 处理
                                    try {
                                        String valStr = (String) fieldVal;
                                        String result = handleSensitiveString(sensitiveField.value(), valStr);
                                        declaredField.set(obj, result);
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public static String handleSensitiveString(SensitiveType type, String val) {
        try {
            String result = "";
            switch (type) {
                case NAME:
                    result = SensitiveUtils.maskName(val);
                    break;
                case MOBILE:
                    result = SensitiveUtils.maskMobile(val);
                    break;
                case EMAIL:
                    result = SensitiveUtils.maskEmail(val);
                    break;
                case IDCARD:
                    result = SensitiveUtils.maskIdCard(val);
                    break;
                case BIRTHDAY:
                    result = SensitiveUtils.maskBirthday(val);
                    break;
                case PASSWORD:
                    result = SensitiveUtils.maskPassword(val);
                    break;
                case BANKCARD:
                    result = SensitiveUtils.maskBankCard(val);
                    break;
                case SALARY:
                    result = SensitiveUtils.maskSalary(val);
                    break;
                case OTHERCARD:
                    result = SensitiveUtils.maskOtherCard(val);
                    break;
                default:
                    result = val;
                    break;
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return val;
        }
    }

}

 

WEB支持类

package cn.com.sensitive;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(9)
@Component
@Slf4j
public class SensitiveAspect {

    @Pointcut("@annotation(cn.com.sensitive.annotations.Sensitive)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object sensitiveAround(ProceedingJoinPoint pjp) throws Throwable {
        Object returnValue = pjp.proceed();
        returnValue = SensitiveAspectSupport.sensitive(pjp, returnValue);
        return returnValue;
    }

}
package cn.com.sensitive;

import cn.com.LogUtils;
import cn.com.sensitive.annotations.Sensitive;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
public class SensitiveAspectSupport {


    public static Object sensitive(JoinPoint pointer, Object returnValue) {

        Long methodStartTime = System.currentTimeMillis();
        Signature signature = pointer.getSignature();
        String methodDesc = signature.getDeclaringTypeName() + "." + signature.getName();
        String msgDesc = "数据脱敏";
        LogUtils.info(log,
                methodDesc,
                msgDesc,
                "arguments:{0} ",
                Arrays.toString(pointer.getArgs()));

        try {
            MethodSignature sign = (MethodSignature) signature;
            Method method = sign.getMethod();
            //获取方法上的注解
            Sensitive sensitive = method.getAnnotation(Sensitive.class);
            if(null != sensitive) {
                SensitiveHandler.handle(returnValue);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        Long methodEndTime = System.currentTimeMillis();
        LogUtils.info(log,
                methodDesc,
                msgDesc,
                " 响应信息:{0}, 方法耗时:{1} 秒", "", (methodEndTime - methodStartTime) / 1000F);
        return returnValue;

    }


}

 

以上是关于基于注解形式的数据脱敏的主要内容,如果未能解决你的问题,请参考以下文章

java 日志脱敏框架 sensitive,优雅的打印脱敏日志

java 日志脱敏框架 sensitive,优雅的打印脱敏日志

java 日志脱敏框架 sensitive-新版本0.0.2-深度拷贝,属性为对象和集合的支持

Jackson注解自定义数据脱敏策略

Jackson注解自定义数据脱敏策略

工具类:使用注解进行数据脱敏(电话号人名等使用****代替)