• 作者:老汪软件技巧
  • 发表时间:2024-09-29 10:01
  • 浏览量:

前言

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

实现方式

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

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

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

代码hutools工具依赖

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

<dependency>
    <groupId>cn.hutoolgroupId>
    <artifactId>hutool-allartifactId>
    <version>5.8.11version>
dependency>

字段脱敏是什么意思__使用自定义注解实现脱敏

@Sensitive注解

/**
 * @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;
}

脱敏策略枚举类

/**
 * @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方法,这个方法就可以在数据写入响应前可以对数据进行处理。

/**
 * @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, Classextends HttpMessageConverter> converterType) {
        // 拦截所有响应
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, org.springframework.http.MediaType selectedContentType,
                                  Classextends 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

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;
       };
    }
}

效果演示