自定义注解+hutool实现字段脱敏

自定义注解+hutool实现字段脱敏

1.引入hutool依赖

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.5</version>
        </dependency>

2.自定义注解

package com.kang.mongodb.annotation;

import cn.hutool.core.util.DesensitizedUtil;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    /**
     * 脱敏作用的类型
     * @return
     */
    DesensitizedUtil.DesensitizedType type();
}
  • 在需要脱敏的实体类中对应的属性字段添加该注解,并指定脱敏类型
@Sensitive(type = DesensitizedUtil.DesensitizedType.MOBILE_PHONE)
private String phone;

3.脱敏工具类

package com.kang.mongodb.utils;

import cn.hutool.core.util.DesensitizedUtil;
import com.kang.mongodb.annotation.Sensitive;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.UnaryOperator;

/**
 * 脱敏工具类
 */
@Slf4j
public class SensitiveUtil {
    private static final Map<String, UnaryOperator<String>> desensitizeMap = new ConcurrentHashMap<>();

    static {
        for (DesensitizedUtil.DesensitizedType type : DesensitizedUtil.DesensitizedType.values()) {
            desensitizeMap.put(type.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, type));
        }
    }

    /**
     * 这里可以使用遍历DesensitizedUtil.DesensitizedType的方式进行初始化,
     * 但是为了以后维护拓展观察方便,就不再使用遍历的方式,手动维护初始化
     */
//    static {
//        // 用户ID
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.USER_ID.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.USER_ID));
//        // 中文名
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.CHINESE_NAME.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.CHINESE_NAME));
//        // 身份证号
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.ID_CARD.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.ID_CARD));
//        // 座机号
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.FIXED_PHONE.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.FIXED_PHONE));
//        // 手机号
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.MOBILE_PHONE.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.MOBILE_PHONE));
//        // 地址
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.ADDRESS.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.ADDRESS));
//        // 电子邮件
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.EMAIL.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.EMAIL));
//        // 密码
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.PASSWORD.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.PASSWORD));
//        // 中国大陆车牌,包含普通车辆、新能源车辆
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.CAR_LICENSE.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.CAR_LICENSE));
//        // 银行卡
//        desensitizeMap.put(DesensitizedUtil.DesensitizedType.BANK_CARD.name(), originalValue -> DesensitizedUtil.desensitized(originalValue, DesensitizedUtil.DesensitizedType.BANK_CARD));
//    }

    /**
     * 脱敏方法
     * @param obj
     * @param <T>
     * @return
     */
    public static <T> T desensitize(T obj) {
        if (obj == null) {
            return null;
        }
        // 利用反射完成注解修饰字段的脱敏
        Class<?> clazz = obj.getClass();
        // 获得类的所有属性,包括private声明的和继承类
        Field[] fields = clazz.getDeclaredFields();
        // 遍历所有的属性字段
        for (Field field : fields) {
            // 判断该类Sensitive中的字段是否声明了指定注解。
            if (field.isAnnotationPresent(Sensitive.class)) {
                // 启动或禁用访问安全检查的开关
                field.setAccessible(true);
                try {
                    // 获取注解中的脱敏方式
                    Sensitive annotation = field.getAnnotation(Sensitive.class);
                    // 如果获取不到脱敏方式,或者脱敏方式为空,不做脱敏操作,返回元数据
                    if (Objects.isNull(annotation) || Objects.isNull(annotation.type()) || Objects.isNull(desensitizeMap.get(annotation.type().name()))) {
                        log.info("脱敏方式未配置,当前字段:{}不执行任何脱敏操做", field.getName());
                        continue;
                    }
                    // 从注解中获取配置的脱敏方式
                    String type = annotation.type().name();
                    // 获取字段的值
                    Object value = field.get(obj);
                    if (!(value instanceof String)) {
                        log.info("非String类型的数据暂不支持数据脱敏");
                        return obj;
                    }
                    // String类型的值
                    // 原始值
                    String originalValue = (String) value;
                    // 自定义加工后的值
                    UnaryOperator<String> operator = desensitizeMap.get(type);
                    // 获取脱敏后的值
                    String desensitizedValue = operator.apply(originalValue);
                    // 重新赋值
                    field.set(obj, desensitizedValue);
                    log.info("当前字段:{},脱敏前的值:{},脱敏后的值:{}", field.getName(), originalValue, desensitizedValue);
                } catch (Exception e) {
                    log.error("执行脱敏操做时发生异常,脱敏异常时为了不影响主流程的正常运行,返回原始值");
                }
            }
        }
        return obj;
    }

    /**
     * 数据脱敏
     * @param value
     * @return
     */
    public static String desensitize(String value,String type) {
        if(StringUtils.isBlank(value) || StringUtils.isBlank(type) || Objects.isNull(desensitizeMap.get(type))){
            log.info("脱敏关键元素缺失,不做脱敏操做");
            return value;
        }
        return desensitizeMap.get(type).apply(value);
    }
}
  • 这里用到了函数式接口编程
  • 使用:UserInfo desensitize = SensitiveUtil.desensitize(userInfo);
  • 使用:String desensitize1 = SensitiveUtil.desensitize(“1378569857”, DesensitizedUtil.DesensitizedType.MOBILE_PHONE.name());

单元测试

package com.kang.mongodb.pojo;

import cn.hutool.core.util.DesensitizedUtil;
import com.kang.mongodb.annotation.Sensitive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

/**
 * @Author Emperor Kang
 * @ClassName UserInfo
 * @Description TODO
 * @Date 2023/9/4 11:35
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {

    @Sensitive(type = DesensitizedUtil.DesensitizedType.CHINESE_NAME)
    private String name;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.MOBILE_PHONE)
    private String phone;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.ADDRESS)
    private String address;

    @Sensitive(type = DesensitizedUtil.DesensitizedType.EMAIL)
    private String email;
}
package com.kang.mongodb;

import cn.hutool.core.util.DesensitizedUtil;
import com.kang.mongodb.pojo.UserInfo;
import com.kang.mongodb.utils.SensitiveUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

/**
 * @Author Emperor Kang
 * @ClassName SensitiveUtilTest
 * @Description TODO
 * @Date 2023/9/4 11:28
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
@Slf4j
public class SensitiveUtilTest {

    @Test
    public void sensitiveTest(){
        UserInfo userInfo = new UserInfo("王冬国", "1378569857", "河南上商丘市民权县葫芦乡葫芦村", "meili@163.com");
        UserInfo desensitize = SensitiveUtil.desensitize(userInfo);
        log.info("脱敏后的数据:{}",desensitize);
        String desensitize1 = SensitiveUtil.desensitize("1378569857", DesensitizedUtil.DesensitizedType.MOBILE_PHONE.name());
        log.info("脱敏后的数据:{}",desensitize1);
    }
}

结果

13:51:38.380 [main] INFO com.kang.mongodb.utils.SensitiveUtil - 当前字段:name,脱敏前的值:王冬国,脱敏后的值:王**
13:51:38.384 [main] INFO com.kang.mongodb.utils.SensitiveUtil - 当前字段:phone,脱敏前的值:1378569857,脱敏后的值:137***9857
13:51:38.384 [main] INFO com.kang.mongodb.utils.SensitiveUtil - 当前字段:address,脱敏前的值:河南上商丘市民权县葫芦乡葫芦村,脱敏后的值:河南上商丘市民********
13:51:38.384 [main] INFO com.kang.mongodb.utils.SensitiveUtil - 当前字段:email,脱敏前的值:meili@163.com,脱敏后的值:m****@163.com
13:51:38.384 [main] INFO com.kang.mongodb.SensitiveUtilTest - 脱敏后的数据:UserInfo(name=王**, phone=137***9857, address=河南上商丘市民********, email=m****@163.com)
13:51:38.390 [main] INFO com.kang.mongodb.SensitiveUtilTest - 脱敏后的数据:137***9857
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值