springboot 注解+切面实现数据脱敏

springboot 注解+切面实现数据脱敏

创建脱敏类型枚举

/**
 * @Description:脱敏类型枚举
 * @Author: huang
 * @Date: 2022-09-01 10:56
 */
public enum  SensitiveTypeEnum {
    /**
     * 中文名
     */
    CHINESE_NAME,
    /**
     * 身份证号
     */
    ID_CARD,
    /**
     * 座机号
     */
    FIXED_PHONE,
    /**
     * 手机号
     */
    MOBILE_PHONE,
    /**
     * 地址
     */
    ADDRESS,
    /**
     * 电子邮件
     */
    EMAIL,
    /**
     * 银行卡
     */
    BANK_CARD,
    /**
     * 公司开户银行联号
     */
    CNAPS_CODE;
}

脱敏注解

/**
 * @Description:脱敏注解
 * @Author: huang
 * @Date: 2022-09-01 10:41
 */
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Desensitized {
    /**
     * 脱敏类型(规则)
     */
    SensitiveTypeEnum type();
}

脱敏工具类

package com.huang.mybatisplus.annotation;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.huang.mybatisplus.http.Response;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description:脱敏工具类
 * @Author: huang
 * @Date: 2022-09-01 10:44
 */
@Slf4j
public class DesensitizedUtil {

    /**
     * 脱敏工具开关,0:关闭,1:开启
     */
    public static final String DESENT_STATUS = "1";

    /**
     * 脱敏数据
     *
     * @param obj
     * @return void
     */
    public static void desentData(Object obj) throws IllegalAccessException {
        if (null == obj) {
            return;
        }
        //如果是原始类型,则忽略处理
        if (obj.getClass().isPrimitive()) {
            return;
        }
        // 是否是接口
        if (obj.getClass().isInterface()) {
            return;
        }
        Object data = null;
        Class<?> clazz = null;
        Field[] fields = null;//所有字段
        //如果是通用的分页响应对象,则对该对象内部的List进行脱敏
        if (obj.getClass().equals(Response.class)) {
            data = ((Response) obj).getData();//这里是自定义返回对象
            clazz = data.getClass();
            if (null == clazz) {
                return;
            }
            // 获取所有属性
            fields = clazz.getDeclaredFields();
        } else {
            //当传递的参数是对象的时候,将当前对象的属性赋值
            data = obj;
            clazz = obj.getClass();
            fields = obj.getClass().getDeclaredFields();
        }
        while (null != clazz.getSuperclass() && !Object.class.equals(clazz.getSuperclass())) {
            fields = (Field[]) ArrayUtils.addAll(fields, clazz.getSuperclass().getDeclaredFields());
            clazz = clazz.getSuperclass();
        }
        if (null == fields && fields.length == 0) {
            return;
        }
        for (Field field : fields) {
            field.setAccessible(true);
            if (null == field) {
                return;
            }
            Object value = field.get(data);
            if (null != value) {
                Class<?> type = value.getClass();
                // 1.处理子属性,包括集合中的
                if (type.isArray()) {
                    int len = Array.getLength(value);
                    for (int i = 0; i < len; i++) {
                        Object arrayObject = Array.get(value, i);
                        DesensitizedUtil.desentData(arrayObject);
                    }
                } else if (value instanceof Collection<?>) {
                    Collection<?> c = (Collection<?>) value;
                    Iterator<?> it = c.iterator();
                    while (it.hasNext()) {
                        Object collectionObj = it.next();
                        //递归实现list 对象中参数脱敏
                        DesensitizedUtil.desentData(collectionObj);
                    }
                } else if (value instanceof Map<?, ?>) {
                    Map<?, ?> m = (Map<?, ?>) value;
                    Set<?> set = m.entrySet();
                    for (Object o : set) {
                        Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
                        Object mapVal = entry.getValue();
                        DesensitizedUtil.desentData(mapVal);
                    }
                } else if (!type.isPrimitive()
                        && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
                        && !StringUtils.startsWith(type.getPackage().getName(), "java.")
                        && !StringUtils.startsWith(field.getType().getName(), "javax.")
                        && !StringUtils.startsWith(field.getName(), "java.")) {
                    DesensitizedUtil.desentData(type);
                }
            }

            // 2. 处理自身的属性
            Desensitized annotation = field.getDeclaredAnnotation(Desensitized.class);
            if (field.getType().equals(String.class) && null != annotation) {
                String valueStr = (String) field.get(data);
                if (StringUtils.isNotBlank(valueStr)) {
                    switch (annotation.type()) {
                        case CHINESE_NAME: {
                            field.set(data, DesensitizedUtil.chineseName(valueStr));
                            break;
                        }
                        case ID_CARD: {
                            field.set(data, DesensitizedUtil.idCardNum(valueStr));
                            break;
                        }
                        case FIXED_PHONE: {
                            field.set(data, DesensitizedUtil.fixedPhone(valueStr));
                            break;
                        }
                        case MOBILE_PHONE: {
                            field.set(data, DesensitizedUtil.mobilePhone(valueStr));
                            break;
                        }
                        case ADDRESS: {
                            field.set(data, DesensitizedUtil.address(valueStr, 4));
                            break;
                        }
                        case EMAIL: {
                            field.set(data, DesensitizedUtil.email(valueStr));
                            break;
                        }
                        case BANK_CARD: {
                            field.set(data, DesensitizedUtil.bankCard(valueStr));
                            break;
                        }
                        case CNAPS_CODE: {
                            field.set(data, DesensitizedUtil.cnapsCode(valueStr));
                            break;
                        }
                        default: {
                            break;
                        }
                    }
                }
            }
        }

    }


    /**
     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
     *
     * @param fullName 中文姓名
     * @return 中文姓名
     */
    private static String chineseName(String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**>
     *
     * @param familyName 中文姓名
     * @param givenName  中文姓名
     * @return
     */
    private static String chineseName(String familyName, String givenName) {
        if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) {
            return "";
        }
        return chineseName(familyName + givenName);
    }

    /**
     * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762>
     *
     * @param id 身份证号
     * @return 身份证号
     */
    private static String idCardNum(String id) {
        if (StringUtils.isBlank(id)) {
            return "";
        }
        String num = StringUtils.right(id, 4);
        return StringUtils.leftPad(num, StringUtils.length(id), "*");
    }

    /**
     * [固定电话] 后四位,其他隐藏<例子:****1234>
     *
     * @param num 固定电话
     * @return 固定电话
     */
    private static String fixedPhone(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*");
    }

    /**
     * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234>
     *
     * @param num 手机号码
     * @return String
     */
    private 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), "*"), "***"));
    }

    /**
     * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****>
     *
     * @param address       地址
     * @param sensitiveSize 敏感信息长度
     * @return String
     */
    private static String address(String address, int sensitiveSize) {
        if (StringUtils.isBlank(address)) {
            return "";
        }
        int length = StringUtils.length(address);
        return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*");
    }
    /**
     * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com>
     *
     * @param email 电子邮箱
     * @return String
     */
    private 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>
     *
     * @param cardNum 银行卡号
     * @return String
     */
    private 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), "*"), "******"));
    }

    /**
     * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********>
     *
     * @param code 卡号
     * @return java.lang.String
     */
    private static String cnapsCode(String code) {
        if (StringUtils.isBlank(code)) {
            return "";
        }
        return StringUtils.rightPad(StringUtils.left(code, 2), StringUtils.length(code), "*");
    }
}

添加是否需要脱敏,方法注解

import java.lang.annotation.*;

/**
 * @Description:
 * @Author: huang
 * @Date: 2022-09-01 11:17
 */
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ToSensitive {

    /**
     * 是否需要脱敏
     */
    boolean isSensitive() default true;
}

切面

import com.huang.mybatisplus.annotation.DesensitizedUtil;
import com.huang.mybatisplus.annotation.ToSensitive;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Description:
 * @Author: huang
 * @Date: 2022-08-31 18:00
 */
@Order(1)
@Aspect
@Slf4j
@Component
public class SensitiveAspect {
    /**
     * 注解脱敏处理
     */
    @Around("execution(public * com.huang.*.controller.*.*(..))")
    public Object sensitiveClass(ProceedingJoinPoint joinPoint) throws Throwable {
        return sensitiveFormat(joinPoint);
    }

    /**
     * 注解统一拦截器
     */
    public Object sensitiveFormat(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //根据注解决定是否需要切面
        if (!method.isAnnotationPresent(ToSensitive.class)) {
            log.info("不需要切面");
            Object result = joinPoint.proceed();
            return result;
        }
        ToSensitive toSensitive = method.getAnnotation(ToSensitive.class);
        if (!toSensitive.isSensitive()){
            log.info("不需要切面脱敏");
            Object result = joinPoint.proceed();
            return result;
        }
        Object obj = joinPoint.proceed();
        if (obj == null || isPrimitive(obj.getClass())) {
            return obj;
        }
        if (DesensitizedUtil.DESENT_STATUS.equals("1")) {
            DesensitizedUtil.desentData(obj);
        }
        return obj;
    }

    /**
     * 基本数据类型和String类型判断
     */
    public static 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;
        }
    }

}

统一结果集和返回值

/**
 * @Description:统一结果集
 * @Author: huang
 * @Date: 2022-09-01 10:46
 */
@Data
@Component
public class Response<T> {
    public static ResponseCode responseCode;
    /**
     * 提示消息
     */
    public String message;

    /**
     * 具体返回的数据
     */
    public T data;

    /**
     * 状态码
     */
    public String code;

    public Response(String code, String message, T data) {
        this.message = message;
        this.code = code;
        this.data = data;
    }

    public Response(String code, String msg) {
        this.message = msg;
        this.code = code;
    }

    @Autowired
    public Response(ResponseCode responseCode) {
        Response.responseCode = responseCode;
    }

    /**
     * 返回成功Response对象
     */
    public static <T> Response<T> success(String successMessage, T data) {
        return new Response<>(responseCode.getSuccessCode(), successMessage, data);
    }

    /**
     * 返回错误Response对象
     */
    public static <T> Response<T> fail(String errorMessage) {
        return new Response<>(responseCode.getErrorCode(), errorMessage);
    }
}
/**
 * @Description:响应码
 * @Author: huang
 * @Date: 2022-09-01 10:47
 */
@Data
@Component
public class ResponseCode {
    public String successCode = "200";

    public String errorCode = "500";

    public String authErrorCode = "300";
}

实体类添加注解

@Data
@TableName("site")
@ApiModel(value = "Site对象", description = "")
public class Site implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "网点ID")
    @TableId(value = "id", type = IdType.INPUT)
    private Long id;

    @Desensitized(type = SensitiveTypeEnum.CHINESE_NAME)
    @ApiModelProperty(value = "网点名称")
    private String name;

    @Desensitized(type = SensitiveTypeEnum.MOBILE_PHONE)
    @ApiModelProperty(value = "网点电话")
    private String tel;
}

controller 添加注解

    @ToSensitive(isSensitive = true) //需要脱敏
    @GetMapping("/site/page")
    public Response<Page<Site>> pageSiteBy() {
        Page<Site> page = siteService.page(new Page<>(0, 3));

        return Response.success("成功", page);
    }

测试结果

springboot 注解+切面实现数据脱敏_第1张图片

你可能感兴趣的:(spring,boot,java,后端)