Redian新闻
>
太强了!一个注解解决数据脱敏问题

太强了!一个注解解决数据脱敏问题

公众号新闻

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.0、JDK 8 + Spring Boot 2.7.18 双版本 

来源:juejin.cn/post/
7225218026097852472


本文主要分享什么是数据脱敏,如何优雅的在项目中运用一个注解实现数据脱敏,为项目进行赋能。希望能给你们带来帮助。

什么是数据脱敏

数据脱敏是一种通过去除或替换敏感数据中的部分信息,以保护数据隐私和安全的技术。其主要目的是确保数据仍然可以在各种场景中使用,同时保护敏感信息,防止数据泄露和滥用。数据脱敏通常用于处理包含个人身份信息和其他敏感信息的数据集,如手机号、姓名、地址、银行卡、身份证号、车牌号等等。

在数据脱敏过程中,通常会采用不同的算法和技术,以根据不同的需求和场景对数据进行处理。例如,对于身份证号码,可以使用掩码算法(masking)将前几位数字保留,其他位用“X”或"*"代替;对于姓名,可以使用伪造(pseudonymization)算法,将真实姓名替换成随机生成的假名。

下面我讲为大家带来数据脱敏掩码操作,让我们一起学起来吧。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

开胃菜

下面给大家介绍的是使用两种不同的工具类进行数据脱敏,而我们今天的主题使用一个注解解决数据脱敏问题的主要两个工具类。来跟着我学习吧。

使用 Hutool 工具类实现数据掩码

比喻说我们现在要对手机号进行数据脱敏,前三后四不掩码,其他全部用 * 进行掩码

如下图代码所示,

我们定义了一个手机号:17677772345,需要进行数据脱敏。

调用的 Hutool 的信息脱敏工具类。

我们运行一下看看结果。一个简单的数据脱敏就实现了。

Hutool 信息脱敏工具类

根据上面的一个 Demo,大家可以看到我使用了 Hutool 的信息脱敏工具类进行对手机号掩码脱敏。那么让我们一起看看 Hutool 信息脱敏的工具类吧。

官网文档:

https://hutool.cn/docs/#/core/工具类/信息脱敏工具-DesensitizedUtil

看一下官网的介绍,支持多种脱敏数据类型,满足我们大部分需求,如果需要自定义还提供了自定义的方法实现。

下面是里面定义号的脱敏规则,直接调用就可以实现简单的数据脱敏,这里给大家介绍是因为我们今天要给大家带来的注解实现数据脱敏核心就是利用我们的 Hutool 提供的工具类实现,支持自定义隐藏。

使用 Jackson 进行数据序列化脱敏

首先创建一个实体类,此实体类只有一个测试的手机号。

注解的讲解:

  • @Data:lombok 的注解生成 get,set 等等方法。
  • @JsonSerialize(using = TestJacksonSerialize.class):该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。可以看下下面的源码,有兴趣的朋友可以去了解一下,也能解决我们日常开发中很多场景。
@Data
public class TestDTO implements Serializable {
    /**
     * 手机号
     */

    @JsonSerialize(using = TestJacksonSerialize.class)
    private String phone
;
}

然后创建一个 TestJacksonSerialize 类实现自定义序列化。

此类主要继承 JsonSerializer,因为我们这里需要序列化的类型是 String 泛型就选择 String。注意如果你使用此注解作用在类上的话,这里就是你要序列化的类。

重写序列化方法,里面的实现很简单就是调用我们的 Hutool 工具类进行手机号数据脱敏。

public class TestJacksonSerialize extends JsonSerializer<String{

    @Override
    @SneakyThrows
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
        // 使用我们的hutool工具类进行手机号脱敏
        jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
    }
}

让我们测试一下吧,因为此注解是运行时生效,我们定义一个接口来测试。

@RestController
@RequestMapping("/test")
public class TestApi {

    @GetMapping
    public TestDTO test(){
        TestDTO testDTO = new TestDTO();
        testDTO.setPhone("17677772345");
        return testDTO;
    }
}

可以看到测试成功,经过上面的两个工具类的介绍,联想一下我们怎么通过两个工具类定义一个自己的注解实现数据脱敏呢。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

注解实现数据脱敏

我们考虑一下,工具类现在有了,那么我们怎么去实现一个注解优雅的解决数据脱敏呢?

请看下文,让我带大家一起学习。

1、定义一个注解

定义一个 Desensitization 注解。

  • @Retention(RetentionPolicy.RUNTIME):运行时生效。
  • @Target(ElementType.FIELD):可用在字段上。
  • @JacksonAnnotationsInside:此注解可以点进去看一下是一个元注解,主要是用户打包其他注解一起使用。
  • @JsonSerialize:上面说到过,该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization 
{
    /**
     * 脱敏数据类型,只要在CUSTOMER的时候,startInclude和endExclude生效
     */

    DesensitizationTypeEnum type() default DesensitizationTypeEnum.CUSTOMER;

    /**
     * 开始位置(包含)
     */

    int startInclude() default 0;

    /**
     * 结束位置(不包含)
     */

    int endExclude() default 0;
}

可以看到此注解有三个值,一个是枚举类定义了我们的脱敏数据类型。一个开始位置,一个结束位置。

枚举类待会给大家讲解,如果选择了自定义类型,下面的开始位置,结束位置才生效。

开始结束位置是我们 Hutool 工具提供的自定义脱敏实现需要的参数。可以看此方法,需要提出一点的是此方法硬编码了掩码值。如果我们的场景需要其他掩码值的话实现也很简单,把 Hutool 的源码拷出来,代替他的硬编码,就可以实现。

2、创建一个枚举类

此枚举类是我们数据脱敏的类型,包括了大部分场景。以及可以满足我们日常开发咯。

public enum DesensitizationTypeEnum {
    //自定义
    CUSTOMER,
    //用户id
    USER_ID,
    //中文名
    CHINESE_NAME,
    //身份证号
    ID_CARD,
    //座机号
    FIXED_PHONE,
    //手机号
    MOBILE_PHONE,
    //地址
    ADDRESS,
    //电子邮件
    EMAIL,
    //密码
    PASSWORD,
    //中国大陆车牌,包含普通车辆、新能源车辆
    CAR_LICENSE,
    //银行卡
    BANK_CARD
}

3、创建我们的自定义序列化类

此类是我们数据脱敏的关键。主要是继承了我们的 JsonSerializer,实现了我的ContextualSerializer。重写了它俩的方法。

  • @NoArgsConstructor:Lombok 无参构造生成。
  • @AllArgsConstructor:Lombok 有参生成。
  • ContextualSerializer:这个类是序列化上下文类,主要是解决我们这个地方获取字段的一些信息,可以看一下源码,他的实现类有很多,Jackson 提供的 @JsonFormat 注解也是实现此类,获取字段的一些信息进行序列化的。有兴趣的朋友可以看一下,多看源码,才能学到 Jackson 的实现方法,才能有今天我们的实现。

两个重写的方法解读:

  • serialize:重写,实现我们的序列化自定义。
  • createContextual:序列化上下文方法重写,获取我们的字段一些信息进行判断,然后返回实例。具体代码可以看下面代码,都有注释噢。
@NoArgsConstructor
@AllArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<Stringimplements ContextualSerializer {
    private DesensitizationTypeEnum type;

    private Integer startInclude;

    private Integer endExclude;
    @Override
    public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        switch (type) {
            // 自定义类型脱敏
            case CUSTOMER:
                jsonGenerator.writeString(CharSequenceUtil.hide(str,startInclude,endExclude));
                break;
            // userId脱敏
            case USER_ID:
                jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId()));
                break;
            // 中文姓名脱敏
            case CHINESE_NAME:
                jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str)));
                break;
            // 身份证脱敏
            case ID_CARD:
                jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 12));
                break;
            // 固定电话脱敏
            case FIXED_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
                break;
            // 手机号脱敏
            case MOBILE_PHONE:
                jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
                break;
            // 地址脱敏
            case ADDRESS:
                jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
                break;
            // 邮箱脱敏
            case EMAIL:
                jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
                break;
            // 密码脱敏
            case PASSWORD:
                jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
                break;
            // 中国车牌脱敏
            case CAR_LICENSE:
                jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
                break;
            // 银行卡脱敏
            case BANK_CARD:
                jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
                break;
            default:
        }

    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            // 判断数据类型是否为String类型
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                // 获取定义的注解
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                // 为null
                if (desensitization == null) {
                    desensitization = beanProperty.getContextAnnotation(Desensitization.class);
                }
                // 不为null
                if (desensitization != null) {
                    // 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
                    return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
                            desensitization.endExclude());
                }
            }

            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }
}

4、测试

创建一个测试注解的 DTO,此测试如下。

@Data
public class TestAnnotationDTO implements Serializable {
    /**
     * 自定义
     */

    @Desensitization(type = DesensitizationTypeEnum.CUSTOMER,startInclude = 5,endExclude = 10)
    private String custom;
    /**
     * 手机号
     */

    @Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
    private String phone;
    /**
     * 邮箱
     */

    @Desensitization(type = DesensitizationTypeEnum.EMAIL)
    private String email;
    /**
     * 身份证
     */

    @Desensitization(type = DesensitizationTypeEnum.ID_CARD)
    private String idCard;
}

新增测试接口:

@GetMapping("/test-annotation")
public TestAnnotationDTO testAnnotation(){
    TestAnnotationDTO testAnnotationDTO = new TestAnnotationDTO();
    testAnnotationDTO.setPhone("17677772345");
    testAnnotationDTO.setCustom("111111111111111111");
    testAnnotationDTO.setEmail("[email protected]");
    testAnnotationDTO.setIdCard("4444199810015555");
    return testAnnotationDTO;
}

测试一下看看效果。如下图所示,完美!

项目 pom 文件

<?xml version="1.0" encoding="utf-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/>
        <!-- lookup parent from repository -->
    </parent>
    <groupId>com.jiaqing</groupId>
    <artifactId>tool-desensitization</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>tool-desensitization</name>
    <description>数据脱敏</description>
    <properties>
        <java.version>1.8</java.version>
        <hutool.version>5.8.5</hutool.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>${hutool.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--json模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

后记

今天给大家带来的是如何实现一个注解进行数据脱敏。

  • 如何使用 hutool 工具类进行数据脱敏。
  • 如何使用 @JsonSerialize 注解实现自定义序列化。
  • 如何使用 hutool 工具+Jackson 实现自己的脱敏注解。

欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
WPS承诺用户文档不会被用于AI训练!隐私政策为何要写“脱敏后作为AI训练”?报税季正式开始!提交前你需要知道这8个注意事项(漢詩英譯) 漫步寒山湖 – 孟朝崗32岁华裔亿万富翁登上Vogue!新郎新娘背景太强了!一份遗书,四个人背诵转达英伟达的VoxFormer太强了!火热的留学与香港移民全注解刷个B站的功夫就能把Python学了,哈佛这门线上课程,太强了!“不要害怕问题,大多数问题都有解决方案”PostgreSQL数据脱敏方式盘点Spring Cache 缓存注解这样用,实在是太香了!【周末综艺会10期】— 面食需要数十亿元资金解决!纽约地铁陷淹水危机:大雨天近一半车站遭淹!纽约300所公校食堂曝重大卫生问题!出现苍蝇蟑螂老鼠等违规问题.今日最佳:共情能力太强了。陈丹琦团队新作:数据量砍95%,大模型性能更强了!Less is More互联网是+,数据要素是x,《三年行动计划》解决数字经济发展难题《幸福永相随》&《数羊》欸,爱马仕家的鞋包配饰又上新了!看看解解馋也是好的...用一体化的设计思维,做一款能解决80%问题的数据库产品拿摄像机对自己一通拍,就能做出一个跟你自由对话的AI NPC?英伟达已经这么强了么刷个B站的功夫就把Python入门了,哈佛的线上课程,太强了!注解方式优雅的实现 Redisson 分布式锁2024年全球最强护照排名揭晓!这6国并列第1!澳洲护照太强了,可畅游189个目的地邂逅相遇的日子(4)第一次咨询前的4个注意事项2024两会热词+注解,看看哪个是你关注的风口?美国报税季正式开始!提交前您需要知道这8个注意事项腾讯安全推出大模型隐私保护脱敏技术国家数据局等17部门“数据要素×”新政:支持数据商上市融资!亿万数据市场来了?太强了!沃顿连续4年蝉联商科第一,这所公立大学让人意想不到!项目终于用上了插入式注解,真香!用一款数据库解决 80%的问题,OceanBase 的底气在哪?红米K70 Pro 测评体验:游戏性能暴涨,大电池长久续航...太强了!手机性能都这么强了,为什么还有人在搞安卓掌机?赶超Gemini Pro,提升推理、OCR能力的LLaVA-1.6太强了
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。