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

    }
}

效果演示

相关推荐
bluebonnet27几秒前
【Rust练习】22.HashMap
开发语言·后端·rust
ifanatic42 分钟前
[面试]-golang基础面试题总结
面试·职场和发展·golang
Iced_Sheep1 小时前
干掉 if else 之策略模式
后端·设计模式
XINGTECODE1 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
程序猿进阶1 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺2 小时前
Spring Boot框架Starter组件整理
java·spring boot·后端
凡人的AI工具箱2 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
先天牛马圣体2 小时前
如何提升大型AI模型的智能水平
后端
java亮小白19972 小时前
Spring循环依赖如何解决的?
java·后端·spring
2301_811274312 小时前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端