Java注解实现敏感数据脱敏

  • Post author:
  • Post category:java


一、创建一个脱敏工具类

package com.example.utils;

import org.apache.commons.lang3.StringUtils;

/**
 * 敏感信息脱敏工具类
 *
 * @Description
 * @Author WL
 * @Date 2023/7/20
 **/

public class MaskUtil {

    /**
     * 手机号脱敏
     * @param phone
     * @return
     */
    public final static String maskPhone(String phone){
        if(phone==null || phone.length()!=11) {
            return phone;
        }
        return StringUtils.join(phone.substring(0,3)+"******"+phone.substring(9));
    }

    /**
     * 身份证号脱敏
     * @param id
     * @return
     */
    public final static String maskIdCard(String id){
        if(id==null || id.length()!=18) {
            return id;
        }
        return StringUtils.join(id.substring(0,6)+"****");
    }

    /**
     *中文姓名脱敏
     * @param fullName
     * @return
     */
    public final static String chineseName(String fullName) {
        if(StringUtils.isBlank(fullName)) {
            return fullName;
        }
        String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     *家庭住址脱敏
     * @param address
     * @param sensitiveSize 敏感信息长度
     * @return
     */
    public final static String address(String address, int sensitiveSize) {
        if(StringUtils.isBlank(address)) {
            return address;
        }
        int length = StringUtils.length(address);
        return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length - sensitiveSize+4, "*");
    }

    /**
     *邮箱脱敏
     * @param email
     * @return
     */
    public final static String email(String email) {
        if (StringUtils.isBlank(email)) {
            return email;
        }
        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)));
        }
    }

    /**
     *银行卡号脱敏
     * @param cardNum
     * @return
     */
    public final static String bankCard(String cardNum) {
        if(StringUtils.isBlank(cardNum)) {
            return cardNum;
        }
        return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******"));
    }

    /**
     *固定电话脱敏
     * @param num
     * @return
     */
    public final static String fixedPhone(String num) {
        if(StringUtils.isBlank(num)) {
            return num;
        }
        return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
    }

    /**
     * 【中国车牌】车牌中间用*代替 eg1:null -》 "" eg1:"" -》 "" eg3:苏D40000 -》 苏D4***0 eg4:陕A12345D -》 陕A1****D eg5:京A123 -》 京A123 如果是错误的车牌,不处理
     *
     * @param carLicense 完整的车牌号
     * @return 脱敏后的车牌号
     */
    public static String carLicense(String carLicense) {
        if (StringUtils.isBlank(carLicense)) {
            return "";
        } else {
            if (carLicense.length() == 7) {
                carLicense = StringUtils.join(carLicense.substring(0,3)+"***"+carLicense.substring(6));
            } else if (carLicense.length() == 8) {
                carLicense = StringUtils.join(carLicense.substring(0,3)+"***"+carLicense.substring(7));
            }

            return carLicense;
        }
    }
}


二、编写自定义注解

package com.example.config.interfaces;

import java.lang.annotation.*;

/**
 * 脱敏信息注解
 *
 * @Description
 * @Author WL
 * @Date 2023/7/20
 **/

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SensitiveField {

    SensitiveTypeEnum value() default SensitiveTypeEnum.IDCARD;

    enum SensitiveTypeEnum {
        IDCARD,
        PHONE,
        NAME,
        ADDRESS,
        BANK,
        CARLICENSE,
    }

}

三、脱敏拦截

package com.example.config.interfaces;


import com.example.utils.MaskUtil;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

/**
 *
 *
 * @Description
 * @Author WL
 * @Date 2023/7/20
 **/
@Aspect
@Configuration
public class SensitiveAspect {
    public static final String ACCESS_EXECUTION = "execution(* com.example..*.*(..))";
    /**
     * 注解脱敏处理
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(ACCESS_EXECUTION)
    public Object sensitiveClass(ProceedingJoinPoint joinPoint) throws Throwable {
        return sensitiveFormat(joinPoint);
    }

    /**
     * 插拔式注解统一拦截器。@SensitiveField
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    public Object sensitiveFormat(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = joinPoint.proceed();
        if (obj == null || isPrimitive(obj.getClass())) {
            return obj;
        }
        for (Field field : obj.getClass().getDeclaredFields()) {
            ReflectionUtils.makeAccessible(field);
            SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
            if (sensitiveField != null) {
                Object value = field.get(obj);
                switch (sensitiveField.value()) {
                    case IDCARD:
                        String idCard = MaskUtil.maskIdCard((String) value);
                        ReflectionUtils.setField(field, obj, idCard);
                        break;
                    case PHONE:
                        String phone = MaskUtil.maskPhone((String) value);
                        ReflectionUtils.setField(field, obj, phone);
                        break;
                    case NAME:
                        String name = MaskUtil.chineseName((String) value);
                        ReflectionUtils.setField(field, obj, name);
                        break;
                    case ADDRESS:
                        //TODO
                        String address = MaskUtil.address((String) value, StringUtils.length((String) value));
                        ReflectionUtils.setField(field, obj, address);
                        break;
                    case BANK:
                        //TODO
                        String bankCard = MaskUtil.bankCard((String) value);
                        ReflectionUtils.setField(field, obj, bankCard);
                        break;
                    case CARLICENSE:
                        //TODO
                        String carLicense = MaskUtil.carLicense((String) value);
                        ReflectionUtils.setField(field, obj, carLicense);
                        break;
                    default:
                        break;
                }
            }
        }
        return obj;
    }


    /**
     * 基本数据类型和String类型判断
     *
     * @param clz
     * @return
     */
    private boolean isPrimitive(Class<?> clz) {
        try {
            if (String.class.isAssignableFrom(clz) || clz.isPrimitive()) {
                return true;
            } else {
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            }
        } catch (Exception e) {
            return false;
        }
    }

}

四、编写一个对象使用注解

package com.example.model;

import com.example.config.interfaces.SensitiveField;
import lombok.Data;

/**
 * TODO
 *
 * @Description
 * @Author WL
 * @Date 2023/7/20
 **/
@Data
public class MskDto {
    @SensitiveField(SensitiveField.SensitiveTypeEnum.IDCARD)
    private String idCard;

    @SensitiveField(SensitiveField.SensitiveTypeEnum.NAME)
    private String complainantName;

    @SensitiveField(SensitiveField.SensitiveTypeEnum.PHONE)
    private String respondentPhone;

    @SensitiveField(SensitiveField.SensitiveTypeEnum.BANK)
    private String bankCard;

    @SensitiveField(SensitiveField.SensitiveTypeEnum.CARLICENSE)
    private String carLicense;
}

五、验证Controller

package com.example.controller;

import com.example.model.MskDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * TODO
 *
 * @Description
 * @Author WL
 * @Date 2023/7/20
 **/
@Slf4j
@RequestMapping("/mas")
@RestController
public class MasController {

    @PostMapping("/test")
    public MskDto sayHelloMqTest(@RequestBody MskDto request) throws Exception{
        log.info("idcard: {}", request.getIdCard().toString());
        log.info("name: {}", request.getComplainantName());
        log.info("phone: {}", request.getRespondentPhone());
        return request;
    }

}

六、验证结果



版权声明:本文为roc_wl原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。