什么是数据脱敏???
数据脱敏(Data Masking)是一种数据安全技术,旨在通过预先设定的规则和算法,对原始数据中包含的敏感信息进行变形处理,使得这些信息在非生产环境(例如开发、测试、分析或培训场景)中使用时,既能保持原有数据格式和业务逻辑的真实性,又能有效保护个人隐私和商业机密不被泄露。
在实施数据脱敏时,对诸如身份证号、手机号、银行卡号、个人姓名、家庭住址、电子邮件地址、医疗记录等敏感信息采取替换、遮盖、乱序、加密、截断等手段进行处理,以防止这些敏感信息在未经授权的情况下被识别或滥用。
具体脱敏规则可以包括但不限于:
-
替换:例如将身份证号的部分数字替换为“*”或随机数。
-
随机化:例如生成与真实数据格式相符但与原始数据无关的新数据。
-
固定模式脱敏:例如保留电话号码的区号和最后几位,其余部分替换。
-
加密:对数据进行不可逆或可逆加密,仅在必要时解密。
-
假名化:用虚构的名字替换真实姓名。
代码实现:主要是使用hutool中的脱敏方法(DesensitizedUtil)来实现
1、首先建立一个spring boot项目
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--json模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
2、创建注解Desensitization
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
//@Target用于限制自定义注解可以应用于哪些程序元素
//ElementType.FIELD:可以应用于字段、枚举常量声明。
@JacksonAnnotationsInside
//作用是将一组注解合并到一个新的注解中。当这个新注解被应用到类或字段上时,实际上相当于应用了它所包含的所有注解。
@JsonSerialize(using = DesensitizationSerialize.class)
//用于自定义 Java 对象如何序列化成 JSON 格式
public @interface Desensitization {
/**
* 脱敏数据类型,只要在CUSTOMER的时候,startInclude和endExclude生效
*/
DesensitizationTypeEnum type() default DesensitizationTypeEnum.CUSTOMER;
/**
* 开始位置(包含)
*/
int startInclude() default 0;
/**
* 结束位置(不包含)
*/
int endExclude() default 0;
}
3.建立脱敏需要的枚举类
public enum DesensitizationTypeEnum {
//自定义
CUSTOMER,
//用户id
USER_ID,
//中文名
CHINESE_NAME,
//身份证号
ID_CARD,
//座机号
FIXED_PHONE,
//手机号
MOBILE_PHONE,
//地址
ADDRESS,
//电子邮件
EMAIL,
//密码
PASSWORD,
//中国大陆车牌,包含普通车辆、新能源车辆
CAR_LICENSE,
//银行卡
BANK_CARD
}
4.创建自定义的序列化脱敏类
@NoArgsConstructor
@AllArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
// 保存敏感信息处理规则的枚举
private DesensitizationTypeEnum type;
private Integer startInclude;
private Integer endExclude;
// JsonGenerator 主要用于生成 JSON 数据并将其输出到各种类型的输出流
//serialize 方法本身并不直接提供数据脱敏功能,它主要是为了将表单数据转换为可传输的字符串格式
@Override
public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
switch (type) {
//自定义 Java 对象如何序列化成 JSON 格式
// 自定义类型脱敏
case CUSTOMER:
//通过使用 CharSequenceUtil 类,开发者可以更加方便地进行字符串相关的编程工作,避免了重复编写基础的字符串处理逻辑
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:
}
}
//BeanProperty 参数代表Java Bean中正在被反序列化的属性, 初始化或定制该反序列化器的行为。
@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) {
//就可以通过 getContextAnnotation 来找到这个注解并据此调整属性的序列化或反序列化行为
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
// 不为null
if (desensitization != null) {
// 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
desensitization.endExclude());
}
}
//查找和提供合适的序列化器(JsonSerializer)来处理给定类型的数据
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
5.测试:创建实例类添加脱敏注解
@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;
}
6、测试类
@RestController
@RequestMapping("/test")
public class TestApi {
@GetMapping("/test-annotation")
public TestAnnotationDTO testAnnotation(){
TestAnnotationDTO testAnnotationDTO = new TestAnnotationDTO();
testAnnotationDTO.setPhone("17634388363");
testAnnotationDTO.setCustom("111111111111111111");
testAnnotationDTO.setEmail("1433926101@qq.com");
testAnnotationDTO.setIdCard("4444199810015555");ce
return testAnnotationDTO;
}
}
7、启动项目:http://localhost:8080/test/test-annotation