实现敏感字段脱敏注解@Sensitive

前言

在B2C项目中,就以电商项目举例,都有前台与后台。并且这类项目的后台往往都会开放给公司内大部分人,甚至有些是将电商项目作为Saas服务提供给外部厂商的,这样后台中记录的用户数据就成为一个风险点,随着越来越多的人可以接触到后台系统,我们必须对用户的数据进行加密不仅限于在数据库层面加密存储,前端展示的时候也必须要对例如:手机号,地址,身份证号等等隐私数据进行脱敏处理。

实现方式

1.最容易想到的就是利用硬编码的形式,哪些接口中涉及到了隐私数据,我们就去接口中对隐私数据进行脱敏。(ps一开始我确实是这么做的)

2.但是我发现太多太多接口都需要使用用户隐私数据了,我人工一个一个手工改也太不优雅了!我就想到我们能不能在SpringMVC将数据写入response的时候就将他拦截住,然后我实现一个注解,其实这个注解也就是一个标识。我们通过反射对于被这个注解标注的字段进行脱敏处理,然后再写回对象中。

这样不就可以只对响应类中加一个注解,然后所有使用用户敏感数据的接口都直接脱敏了吗,而且我们也可以很方便的改变我们的脱敏策略!!!

代码

hutools工具依赖

最适合中国宝宝体质的中国工具包,虽然网上很多人喷他,但是我个人觉得还是挺好用的,可能是我段位还不够。

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.11</version>
</dependency>

@Sensitive注解

less 复制代码
/**
 * @projectName: BlossomKnowledge
 * @package: blossom.project.bk.common.annotaion
 * @className: Sensitive
 * @author: Link Ji
 * @description: GOGO
 * @VX: _Aeeee86
 * @date: 2024/9/28 16:36
 * @version: 1.0
 */
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    SensitiveDataType type() default SensitiveDataType.PASSWORD;

}

脱敏策略枚举类

typescript 复制代码
/**
 * @projectName: BlossomKnowledge
 * @package: blossom.project.bk.common.enums
 * @className: SensitiveDataType
 * @author: Link Ji
 * @description: GOGO
 * @VX: _Aeeee86
 * @date: 2024/9/28 16:40
 * @version: 1.0
 */
public enum SensitiveDataType {
    //脱敏数据类型
    NAME("name"),
    ID_CARD("idCard"),
    PHONE("phone"),
    EMAIL("email"),
    BANK_CARD("bankCard"),
    ADDRESS("address"),
    PASSWORD("password"),
    ;

    SensitiveDataType(String type) {
        this.type = type;
    }
    @Getter
    private String type;
}

响应拦截器

这里就是最核心的代码了,利用了SpringMVC提供的钩子接口,ResponseBodyAdvice接口,其中提供了一个beforeBodyWrite方法,这个方法就可以在数据写入响应前可以对数据进行处理。

typescript 复制代码
/**
 * @projectName: BlossomKnowledge
 * @package: blossom.project.bk.common.enums
 * @className: SensitiveDataType
 * @author: Link Ji
 * @description: GOGO
 * @VX: _Aeeee86
 * @date: 2024/9/28 16:40
 * @version: 1.0
 */
@ControllerAdvice
public class SensitiveDataAdvice implements ResponseBodyAdvice<Object> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 拦截所有响应
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, org.springframework.http.MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  org.springframework.http.server.ServerHttpRequest request,
                                  org.springframework.http.server.ServerHttpResponse response) {
        // 如果返回类型是result
        if (body instanceof Result<?>){
            // 处理对象,进行脱敏操作
            handleSensitiveFields((Result<?>) body);
        }

        return body;
    }

    private void handleSensitiveFields(Result<?> res) {
        Object data = res.getData();
        //获取data的下的全部字段
        if (data == null) {
            return;
        }
        Field[] fields = data.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 判断是否有 @SensitiveData 注解
            if (field.isAnnotationPresent(Sensitive.class)) {
                Sensitive annotation = field.getAnnotation(Sensitive.class);
                SensitiveDataType sensitiveDataType = annotation.type();
                field.setAccessible(true);
                try {
                    Object value = field.get(data);
                    if (value instanceof String) {
                        // 执行脱敏操作
                        String maskedValue = DesensitizationUtils.maskData((String) value, sensitiveDataType.getType());
                        field.set(data, maskedValue);
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

脱敏工具类

这个工具类依赖于hutools提供的DesensitizedUtil

typescript 复制代码
public class DesensitizationUtils {
    
    public static String maskData(String data, String type) {
        if (data == null) {
            return null;
        }
        //使用switch匹配SensitiveDataType枚举中的类型,并且使用hutool脱敏工具类进行脱敏
       return switch (type) {
           case "name" -> DesensitizedUtil.chineseName(data);
           case "idCard" -> DesensitizedUtil.idCardNum(data, 2, data.length() - 2);
           case "phone" -> DesensitizedUtil.mobilePhone(data);
           case "email" -> DesensitizedUtil.email(data);
           case "bankCard"-> DesensitizedUtil.bankCard(data);
           case "address" -> DesensitizedUtil.address(data, data.length() - 6);
           default -> data;
       };

    }
}

效果演示

相关推荐
丘山子42 分钟前
一些鲜为人知的 IP 地址怪异写法
前端·后端·tcp/ip
CopyLower1 小时前
在 Spring Boot 中实现 WebSockets
spring boot·后端·iphone
天天扭码2 小时前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
.生产的驴2 小时前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑2 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
追逐时光者3 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
天天扭码3 小时前
深入讲解Javascript中的常用数组操作函数
前端·javascript·面试
方圆想当图灵3 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫3 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
渭雨轻尘_学习计算机ing3 小时前
二叉树的最大宽度计算
算法·面试