实现敏感字段脱敏注解@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;
       };

    }
}

效果演示

相关推荐
man20171 小时前
【2024最新】基于springboot+vue的闲一品交易平台lw+ppt
vue.js·spring boot·后端
hlsd#1 小时前
关于 SpringBoot 时间处理的总结
java·spring boot·后端
路在脚下@1 小时前
Spring Boot 的核心原理和工作机制
java·spring boot·后端
幸运小圣2 小时前
Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】
开发语言·后端·rust
前端SkyRain3 小时前
后端Node学习项目-用户管理-增删改查
后端·学习·node.js
提笔惊蚂蚁3 小时前
结构化(经典)软件开发方法: 需求分析阶段+设计阶段
后端·学习·需求分析
老猿讲编程3 小时前
Rust编写的贪吃蛇小游戏源代码解读
开发语言·后端·rust
黄小耶@3 小时前
python如何使用Rabbitmq
分布式·后端·python·rabbitmq
saturday-yh3 小时前
性能优化、安全
前端·面试·性能优化
宅小海4 小时前
Scala-List列表
开发语言·后端·scala