数据脱敏
在介绍下面内容之前,先谈谈我对脱敏的理解吧,让大家也能了解我学习这个技术的动力吧!
**敏是敏感词**的意思,而脱敏就是为了防止数据泄露的一种方式。
那么,为什么进行脱敏?
生活中我们的手机绑定银行卡号中间几位是不是看不到呢?有时候我们是不是也会因此而恼火呢?
就让我们了解*是怎么出来的吧!!!
根据搜索结果,MyBatis-Flex、Hutool库和Jackson是三种常用的脱敏方式,它们各有特点和应用场景。以下是它们的比较:
-
MyBatis-Flex:
- 提供了
@ColumnMask()注解,并内置了9种脱敏规则,如手机号、固定电话、身份证号、车牌号、地址、邮件、密码和银行卡号脱敏。 - 支持自定义脱敏规则,通过
MaskManager注册新的脱敏规则。 - 适用于ORM场景,特别是在数据访问层对查询结果进行脱敏处理。
- 提供了
-
Hutool库:
- 提供了
DesensitizedUtil工具类,支持多种脱敏数据类型,包括用户id、中文姓名、身份证号、座机号、手机号、地址、电子邮件、密码、中国大陆车牌和银行卡。 - 通过静态方法一行代码实现脱敏,如
DesensitizedUtil.mobilePhone()、DesensitizedUtil.bankCard()等。 - 适用于需要快速、灵活进行数据脱敏的场景,特别是在Java代码中直接处理数据脱敏。
- 提供了
-
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)
-
目的:
- 数据脱敏的目的是隐藏敏感数据的特定信息,使其在非生产环境中(如开发、测试或培训环境)无法被识别,同时保留数据的格式和某些属性,以便进行开发、测试和分析。
-
实现方式:
- 脱敏通常涉及替换、遮盖或修改数据集中的敏感字段,例如将姓名、电话号码、信用卡号等替换为假数据或部分掩码数据。
-
使用场景:
- 脱敏数据常用于确保在非生产环境中处理数据时遵守数据隐私法规,如GDPR或HIPAA,同时允许开发人员和测试人员使用真实的数据结构和流程。
数据加密(Data Encryption)
-
目的:
- 数据加密的目的是保护数据的机密性,防止未经授权的访问和读取。加密确保只有拥有正确密钥的个人或系统才能解密和访问原始数据。
-
实现方式:
- 加密涉及使用算法和密钥将数据转换成不可读的格式。数据在存储或传输过程中被加密,而在需要使用数据时再进行解密。
-
使用场景:
- 加密适用于保护存储在数据库、硬盘驱动器或通过网络传输的数据。它对于防止数据在未经授权的情况下被访问至关重要。
主要区别
- 可读性:脱敏数据通常对最终用户是不可知的,而加密数据可以被授权用户通过解密过程访问原始数据。
- 用途:脱敏用于在不同环境中模拟生产数据的外观和行为,而加密用于在数据存储和传输过程中保护数据的安全性。
- 法规遵从:脱敏有助于遵守某些数据隐私法规,而加密则用于满足数据安全和保密性的要求。
- 性能影响:加密可能会对系统性能产生影响,因为它需要额外的处理来加密和解密数据,而脱敏通常对性能影响较小。
总的来说,数据脱敏和加密是互补的技术,它们可以一起使用,以提供更全面的数据保护策略。在实际应用中,根据数据的敏感性、使用场景和安全要求,可能会同时采用脱敏和加密措施。
四.MyBatis实现脱敏
MyBatis 可以实现数据脱敏,主要通过使用 MyBatis 插件和注解的方式来完成。以下是实现数据脱敏的基本原理和步骤:
-
定义脱敏注解:创建一个注解,用于标记需要脱敏的字段。
-
实现脱敏策略:定义不同的脱敏策略,例如手机号、身份证号等的脱敏规则。
-
编写脱敏插件:实现 MyBatis 的
Interceptor接口,拦截ResultSetHandler类的handleResultSets方法,对查询结果进行脱敏处理。 -
应用脱敏注解:在实体类的字段上使用脱敏注解,标识需要脱敏的字段。
-
配置 MyBatis 插件:将脱敏插件注册为 MyBatis 插件,以便在查询时自动应用脱敏规则。
-
处理结果集:在插件中,对结果集中的每个对象进行遍历,对标记为脱敏的字段应用脱敏策略。
通过上述步骤,MyBatis 在查询数据时会自动对敏感字段进行脱敏处理,保护用户隐私和数据安全。这种方法的优点是可以透明地集成到现有的 MyBatis 使用中,无需修改大量的业务代码,只需通过配置和注解即可实现数据脱敏。
五.Apache ShardingSphere
Apache ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 Sharding-JDBC、Sharding-Proxy 和 Sharding-Sidecar(计划中)这 3 款相互独立的产品组成。
他们均提供标准化的数据分片、分布式事务和数据库治理功能 。Apache ShardingSphere 下面存在一个数据脱敏模块,此模块集成的常用的数据脱敏的功能。
其基本原理是对用户输入的 SQL 进行解析拦截,并依靠用户的脱敏配置进行 SQL 的改写,从而实现对原文字段的加密及加密字段的解密。
最终实现对用户无感的加解密存储、查询。通过 Apache ShardingSphere 可以自动化&透明化数据脱敏过程,用户无需关注脱敏中间实现细节。并且,提供了多种内置、第三方(AKS)的脱敏策略,用户仅需简单配置即可使用

浙公网安备 33010602011771号