数据脱敏

  在介绍下面内容之前,先谈谈我对脱敏的理解吧,让大家也能了解我学习这个技术的动力吧!
  **敏是敏感词**的意思,而脱敏就是为了防止数据泄露的一种方式。
  那么,为什么进行脱敏?
  生活中我们的手机绑定银行卡号中间几位是不是看不到呢?有时候我们是不是也会因此而恼火呢?
  就让我们了解*是怎么出来的吧!!!

根据搜索结果,MyBatis-Flex、Hutool库和Jackson是三种常用的脱敏方式,它们各有特点和应用场景。以下是它们的比较:

  1. MyBatis-Flex

    • 提供了@ColumnMask()注解,并内置了9种脱敏规则,如手机号、固定电话、身份证号、车牌号、地址、邮件、密码和银行卡号脱敏。
    • 支持自定义脱敏规则,通过MaskManager注册新的脱敏规则。
    • 适用于ORM场景,特别是在数据访问层对查询结果进行脱敏处理。
  2. Hutool库

    • 提供了DesensitizedUtil工具类,支持多种脱敏数据类型,包括用户id、中文姓名、身份证号、座机号、手机号、地址、电子邮件、密码、中国大陆车牌和银行卡。
    • 通过静态方法一行代码实现脱敏,如DesensitizedUtil.mobilePhone()DesensitizedUtil.bankCard()等。
    • 适用于需要快速、灵活进行数据脱敏的场景,特别是在Java代码中直接处理数据脱敏。
  3. Jackson

    • 通过自定义序列化器和注解方式实现脱敏,可以在JSON序列化时进行数据脱敏。
    • 支持自定义脱敏策略,通过定义策略类和工厂类来实现不同的脱敏逻辑。
    • 适用于Web应用中,特别是在对象转换为JSON响应体时对敏感数据进行脱敏。

综合来看,Hutool库因其简单易用和广泛的脱敏数据类型支持,可能是最常用的脱敏方式。它提供了一行代码就能完成脱敏的便捷方法,适用于多种常见的敏感信息脱敏场景。而MyBatis-Flex和Jackson则更多地用于特定的ORM和JSON序列化场景。因此,如果要选择最常用的脱敏方式,Hutool库可能是首选。

基于SpringBoot

一.Hutool库

  • 配置Maven依赖
<!-- Hutool工具类库 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.16</version> <!-- 请使用最新的版本号 -->
        </dependency>
  • 创建一个测试类
package com.curry.desensitization;

import cn.hutool.core.util.DesensitizedUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:00
 * @Description:hutool脱敏工具测试类
 */
@SpringBootTest //启动测试类
public class HuToolDesensitizationTest {
    /**
     * 测试手机号脱敏
     */

    @Test
    public void testPhoneDesensitization() {
        String phone="13723231234";
        System.out.println(DesensitizedUtil.mobilePhone(phone));
        //输出:137****1234
    }
    /**
     * 测试银行卡号脱敏
     */

    @Test
    public void testBankCardDesensitization() {
        String bankCard="6217000130008255666";
        System.out.println(DesensitizedUtil.bankCard(bankCard)); 
        //输出:6217 **** **** *** 5666
    }

    /**
     * 测试身份证号脱敏
     */

    @Test
    public void testIdCardNumDesensitization() {
        String idCardNum="411021199901102321";
        //只显示前4位和后2位
        System.out.println(DesensitizedUtil.idCardNum(idCardNum,4,2)); 
        //输出:4110************21
    }

    /**
     * 测试邮箱脱敏
     */
    @Test
    public void testPasswordDesensitization() {
        String password="www.jd.com_35711";
        System.out.println(DesensitizedUtil.password(password)); 
        //输出:****************
    }
}

运行结果为:

二.配合JackSon通过注解方式实现脱敏

项目是基于 Spring Boot 的 web 项目,则可以利用 Spring Boot 自带的 jackson 自定义序列化实现。它的实现原理其实就是在 json 进行序列化渲染给前端时,进行脱敏

第一步:脱敏策略的枚举(建立一个util包,在util包下创建DesensitizationTypeEnum枚举类)

package com.curry.util;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:06
 * @Description:脱敏策略枚举
 */
public enum DesensitizationTypeEnum {
    //自定义
    MY_RULE,
    //用户id
    USER_ID,
    //中文名
    CHINESE_NAME,
    //身份证号
    ID_CARD,
    //座机号
    FIXED_PHONE,
    //手机号
    MOBILE_PHONE,
    //地址
    ADDRESS,
    //电子邮件
    EMAIL,
    //密码
    PASSWORD,
    //中国大陆车牌,包含普通车辆、新能源车辆
    CAR_LICENSE,
    //银行卡
    BANK_CARD
}

第二步:定义一个用于脱敏的 Desensitization 注解

@Retention (RetentionPolicy.RUNTIME):运行时生效。

@Target (ElementType.FIELD):可用在字段上。

@JacksonAnnotationsInside:此注解可以点进去看一下是一个元注解,主要是用户打包其他注解一起使用。

@JsonSerialize:上面说到过,该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。

package com.curry.desensitization;

import com.curry.util.DesensitizationTypeEnum;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:08
 * @Description:desensitization注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
    /**
     * 脱敏数据类型,在MY_RULE的时候,startInclude和endExclude生效
     */
    DesensitizationTypeEnum type() default DesensitizationTypeEnum.MY_RULE;

    /**
     * 脱敏开始位置(包含)
     */
    int startInclude() default 0;

    /**
     * 脱敏结束位置(不包含)
     */
    int endExclude() default 0;
}

注:只有使用了自定义的脱敏枚举 MY_RULE 的时候,开始位置和结束位置才生效

第三步:创建自定的序列化类

这一步是我们实现数据脱敏的关键。自定义序列化类继承 JsonSerializer,实现 ContextualSerializer 接口,并重写两个方法

新建DesensitizationSerialize类

package com.curry.desensitization;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:10
 * @Description:
 */

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
import com.curry.util.DesensitizationTypeEnum;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.util.Objects;

/**
 * @author
 * @description: 自定义序列化类
 */
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements 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 MY_RULE:
                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), 1, 2));
                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);
    }
}

首先定义一个要测试的 pojo,对应的字段加入要脱敏的策略

package com.curry.Entity;

import com.curry.desensitization.Desensitization;
import com.curry.util.DesensitizationTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:16
 * @Description:测试pojo类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestPojo {

    private String userName;

    @Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
    private String phone;

    @Desensitization(type = DesensitizationTypeEnum.PASSWORD)
    private String password;

    @Desensitization(type = DesensitizationTypeEnum.MY_RULE, startInclude = 0, endExclude = 2)
    private String address;
}

写一个测试的controller

package com.curry.controller;

import com.curry.Entity.TestPojo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: 蓝影
 * @Date: 2024/11/5 19:15
 * @Description:controller测试类
 */
@RestController
public class TestController {

    @PostMapping("/test")
    public TestPojo testDesensitization(){
        TestPojo testPojo = new TestPojo();
        testPojo.setUserName("我是用户名");
        testPojo.setAddress("地球中国-北京市通州区京东总部2号楼");
        testPojo.setPhone("13782946666");
        testPojo.setPassword("sunyangwei123123123.");
        System.out.println(testPojo);
        return testPojo;
    }

}

在postman测试结果为

脱敏和加密的区别

以下是数据脱敏和加密的区别,以表格形式展现:

特性 数据脱敏 数据加密
目的 隐藏敏感数据的具体信息,防止敏感数据在非生产环境中被识别 保护数据的机密性,防止未经授权的访问和读取
实现方式 替换、遮盖或修改数据集中的敏感字段,保留数据的格式和某些属性 使用算法和密钥将数据转换成不可读的格式
数据可读性 脱敏后的数据对最终用户是不可知的,通常用于非生产环境 加密后的数据可以被授权用户通过解密过程访问原始数据
用途 在开发、测试或培训环境中使用,以确保数据隐私法规的遵守 在数据存储和传输过程中保护数据的安全性
性能影响 通常对性能影响较小,因为只是数据的显示方式发生了变化 可能会对系统性能产生影响,因为需要额外的处理来加密和解密数据
法规遵从 有助于遵守数据隐私法规,如GDPR或HIPAA 用于满足数据安全和保密性的要求
数据恢复 脱敏数据通常不可逆,无法恢复原始数据 加密数据可以通过解密过程恢复原始数据
使用场景 适用于需要模拟生产数据的外观和行为,但不暴露实际数据的场景 适用于需要保护数据不被未授权访问的场景,如金融交易、个人身份信息等
技术实现 可以手动实现,也可以使用工具或库来自动实现脱敏规则 需要使用加密算法(如AES、RSA)和密钥管理
数据格式 脱敏后的数据通常保留原始数据的格式 加密后的数据格式可能会改变,通常是二进制格式

通过这个表格,可以清楚地看到数据脱敏和加密在目的、实现方式、数据可读性、用途、性能影响、法规遵从、数据恢复、使用场景和技术实现等方面的区别。这两种技术通常结合使用,以提供全面的数据保护策略。

数据脱敏(Data Masking)和数据加密(Data Encryption)是两种不同的数据保护技术,它们在目的、实现方式和使用场景上有所区别:

数据脱敏(Data Masking)

  1. 目的

    • 数据脱敏的目的是隐藏敏感数据的特定信息,使其在非生产环境中(如开发、测试或培训环境)无法被识别,同时保留数据的格式和某些属性,以便进行开发、测试和分析。
  2. 实现方式

    • 脱敏通常涉及替换、遮盖或修改数据集中的敏感字段,例如将姓名、电话号码、信用卡号等替换为假数据或部分掩码数据。
  3. 使用场景

    • 脱敏数据常用于确保在非生产环境中处理数据时遵守数据隐私法规,如GDPR或HIPAA,同时允许开发人员和测试人员使用真实的数据结构和流程。

数据加密(Data Encryption)

  1. 目的

    • 数据加密的目的是保护数据的机密性,防止未经授权的访问和读取。加密确保只有拥有正确密钥的个人或系统才能解密和访问原始数据。
  2. 实现方式

    • 加密涉及使用算法和密钥将数据转换成不可读的格式。数据在存储或传输过程中被加密,而在需要使用数据时再进行解密。
  3. 使用场景

    • 加密适用于保护存储在数据库、硬盘驱动器或通过网络传输的数据。它对于防止数据在未经授权的情况下被访问至关重要。

主要区别

  • 可读性:脱敏数据通常对最终用户是不可知的,而加密数据可以被授权用户通过解密过程访问原始数据。
  • 用途:脱敏用于在不同环境中模拟生产数据的外观和行为,而加密用于在数据存储和传输过程中保护数据的安全性。
  • 法规遵从:脱敏有助于遵守某些数据隐私法规,而加密则用于满足数据安全和保密性的要求。
  • 性能影响:加密可能会对系统性能产生影响,因为它需要额外的处理来加密和解密数据,而脱敏通常对性能影响较小。

总的来说,数据脱敏和加密是互补的技术,它们可以一起使用,以提供更全面的数据保护策略。在实际应用中,根据数据的敏感性、使用场景和安全要求,可能会同时采用脱敏和加密措施。

四.MyBatis实现脱敏

MyBatis 可以实现数据脱敏,主要通过使用 MyBatis 插件和注解的方式来完成。以下是实现数据脱敏的基本原理和步骤:

  1. 定义脱敏注解:创建一个注解,用于标记需要脱敏的字段。

  2. 实现脱敏策略:定义不同的脱敏策略,例如手机号、身份证号等的脱敏规则。

  3. 编写脱敏插件:实现 MyBatis 的 Interceptor 接口,拦截 ResultSetHandler 类的 handleResultSets 方法,对查询结果进行脱敏处理。

  4. 应用脱敏注解:在实体类的字段上使用脱敏注解,标识需要脱敏的字段。

  5. 配置 MyBatis 插件:将脱敏插件注册为 MyBatis 插件,以便在查询时自动应用脱敏规则。

  6. 处理结果集:在插件中,对结果集中的每个对象进行遍历,对标记为脱敏的字段应用脱敏策略。

通过上述步骤,MyBatis 在查询数据时会自动对敏感字段进行脱敏处理,保护用户隐私和数据安全。这种方法的优点是可以透明地集成到现有的 MyBatis 使用中,无需修改大量的业务代码,只需通过配置和注解即可实现数据脱敏。

五.Apache ShardingSphere

Apache ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar(计划中)这 3 款相互独立的产品组成。

他们均提供标准化的数据分片、分布式事务和数据库治理功能 。Apache ShardingSphere 下面存在一个数据脱敏模块,此模块集成的常用的数据脱敏的功能。

其基本原理是对用户输入的 SQL 进行解析拦截,并依靠用户的脱敏配置进行 SQL 的改写,从而实现对原文字段的加密及加密字段的解密。

最终实现对用户无感的加解密存储、查询。通过 Apache ShardingSphere 可以自动化&透明化数据脱敏过程,用户无需关注脱敏中间实现细节。并且,提供了多种内置、第三方(AKS)的脱敏策略,用户仅需简单配置即可使用

posted @ 2024-11-06 14:01  curry库-04049  阅读(174)  评论(0)    收藏  举报