AOP实现脱敏(非拦截器方式)

本文介绍了一种简单的基于注解的接口返回数据脱敏处理方法,用于保护敏感信息如电话、邮箱等。通过创建注解、枚举脱敏类型以及切面处理,实现了对响应数据中特定字段的脱敏。使用时只需在Controller方法和返回对象的字段上添加注解,即可自动处理敏感数据。

        项目开发过程中有时需要对接口返回数据进行脱敏处理,目的是有一些重要数据比如:电话、邮箱、车牌、密码等等不能被用户访问,不要说网站需要登录外部用户没法访问,你要记住,在网络上任何数据都是不安全的。

        之前的项目用的是拦截器模式做的脱敏,今天闲来无事做了一个稍微简单点的基于注解的脱敏处理工具。

        下面的两个类是使用的拦截器方式,项目中可以选择使用,拦截器脱敏推荐看https://blog.csdn.net/a5932067/article/details/118970044,我这里做的是纯注解方式,不使用拦截器,写的不好,有需要改进的欢迎评论 。

一。创建注解

@Target({ElementType.FIELD,ElementType.METHOD})//必须是方法配合字段同时注解
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
/**
 * 脱敏注解
 *  使用流程:
 *      1.注解返回方法 填写需要脱敏的类
 *      2.注解需要脱敏的字段 填写规则
 *
 * @author: tq
 * @description:
 * @date: 2021/8/6 10:43
 */
public @interface Desensitized {
    //脱敏类型(规则)
    SensitiveTypeEnum type() default SensitiveTypeEnum.NO;

    //被脱敏的class
    Class<?> targetClass() default Void.class;
}

二。脱敏枚举

/**
 * 脱敏枚举类 定义可被脱敏的类型
 *
 * @author: tq
 * @description: 
 * @date: 2021/8/11 17:10
 */
public enum SensitiveTypeEnum {
    /**
     * 中文名
     */
    CHINESE_NAME,
    /**
     * 身份证号
     */
    ID_CARD,
    /**
     * 座机号
     */
    FIXED_PHONE,
    /**
     * 手机号
     */
    MOBILE_PHONE,
    /**
     * 地址
     */
    ADDRESS,
    /**
     * 电子邮件
     */
    EMAIL,
    /**
     * 银行卡
     */
    BANK_CARD,
    /**
     * 密码
     */
    PASSWORD,
    /**
     * 车牌号
     */
    CARNUMBER,
    /**
     * 不需要脱敏 或者方法注册
     */
    NO;

}

三。切面处理

/**
 * 注解实际处理类 Map类型数据请绕道
 *
 * @author tq
 * @since 2021年1月22日
 */
@Aspect
@Component
@Slf4j
public class DesensitizedHandler {

    @Autowired
    private ApplicationContext applicationContext;

    @Pointcut("@annotation(填写注解位置)")
    public void desensitizedPoint() {

    }

    @AfterReturning(returning = "data", pointcut = "desensitizedPoint()")
    public Object around(JoinPoint joinPoint, Object data) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法的标签
        Desensitized desensitized = signature.getMethod().getAnnotation(Desensitized.class);
        //这里有局限性 只能是满足统一返回的类才能被用于检查
        if (data instanceof ResponseMessage) {
            ResponseMessage responseMessage = (ResponseMessage) data;
            this.setFieldValueForCollection(desensitized, responseMessage.getResult());
        } else {
            return data;
        }
        return data;
    }

    /**
     * 查询并赋值操作
     *
     * @param data 目标数据
     */
    private void setFieldValueForCollection(Desensitized desensitized, Object data) throws Exception {
        Class targetClass = desensitized.targetClass();
        //确定不是集合 就直接获取字段
        if (null == data) {
            return;
        }
        Class clazz = data.getClass();
        if (clazz == Field.class) {
            clazz = ((Field) data).getType();
        }
        if (Collection.class.isAssignableFrom(clazz) || clazz.isArray()) {
            //如果是集合 则循环获取
            Collection collection = (Collection) data;
            //这里不要判断空 因为 data已经判断过
            if (collection.isEmpty()) {return;}
            Iterator iterator = collection.iterator();
            while (iterator.hasNext()) {
                Object next = iterator.next();
                setFieldValueForCollection(desensitized, next);
            }
        } else {
            if (clazz != targetClass) {
                return;
            }
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                //如果字段是空的 就不处理下一个
                if (null == field.getType() || null == field.get(data)) {
                    continue;
                }
                //判断字段类型是否为集合
                if (Collection.class.isAssignableFrom(field.getType()) || field.getType().isArray()) {
                    //如果是集合 则循环获取
                    Object dataIn = field.get(data);
                    setFieldValueForCollection(desensitized, dataIn);
                } else {
                    Desensitized desensitization;
                    if (String.class != field.getType() || (desensitization = field.getAnnotation(Desensitized.class)) == null) {
                        continue;
                    }
                    field.setAccessible(true);// 暴力拆解
//                  String targetField = field.getName();// realName
                    String value = (String) field.get(data);
                    SensitiveTypeEnum type = desensitization.type();
                    //这里如果嫌长就放到util中
                    switch (type) {
                        case CHINESE_NAME:
                            value = DesensitizedUtils.chineseName(value);
                            break;
                        case ID_CARD:
                            value = DesensitizedUtils.idCardNum(value);
                            break;
                        case FIXED_PHONE:
                            value = DesensitizedUtils.fixedPhone(value);
                            break;
                        case MOBILE_PHONE:
                            value = DesensitizedUtils.mobilePhone(value);
                            break;
                        case ADDRESS:
                            value = DesensitizedUtils.address(value, 8);
                            break;
                        case EMAIL:
                            value = DesensitizedUtils.email(value);
                            break;
                        case BANK_CARD:
                            value = DesensitizedUtils.bankCard(value);
                            break;
                        case PASSWORD:
                            value = DesensitizedUtils.password(value);
                            break;
                        case CARNUMBER:
                            value = DesensitizedUtils.carNumber(value);
                            break;
                        default:
                    }
                    field.set(data, value);
                }

            }
        }

    }
}

四。使用

controller的方法上添加注解,并制定需要被脱敏的类,ResponseMessage是统一返回类,具体返回的数据一般都是被包装到内部。

 

具体返回类中的字段使用注解标明此字段需要以什么类型被脱敏。

 

 主要流程为:利用@AfterReturning获取返回的数据-校验数据类型-循环校验-读取到被注解的字段-重新设置值。有一些局限性,如果需要不同项目使用的需要做一些小修改,后面有空再改改,看能不能改成通用的,欢迎大家给建议。

         

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值