什么是数据脱敏
数据脱敏是指对某些敏感信息,例如姓名、身份证号码、手机号、固定电话、银行卡号、邮箱等个人信息,通过脱敏算法进行数据变形
,以保护敏感隐私数据。
数据脱敏通常涉及以下几种主要方法:
- 替换: 将原始数据中的敏感信息替换为不敏感的等效数据。例如,将真实姓名替换为随机生成的名称,将电话号码替换为虚构的号码。
- 扰动: 对数据进行微小的变化,以使其仍然保持某种程度的统计一致性,但不足以使个人身份可被轻松识别。这包括添加噪声或对数值进行微小的随机化。
- 屏蔽: 将敏感信息从数据中删除或隐藏。例如,用特定字符或占位符替换敏感文本。
- 一般化: 减少数据的精确度,使得数据更加模糊。例如,将年龄精确到天数的数据一般化为年龄范围。
- 删除: 完全删除不必要的敏感信息。
Spring Boot自定义注解实现数据脱敏
依赖版本
- JDK 17
- Spring Boot 3.2.0
- Hutool-core 5.8.24 (非必须)
源码地址:Gitee
导入依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</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>5.8.24</version>
</dependency>
</dependencies>
自定义脱敏类型枚举
/**
* @description 数据脱敏策略枚举
*/
public enum DesensitizationTypeEnum {
//自定义
CUSTOM,
//用户id
USER_ID,
//中文名
CHINESE_NAME,
//身份证号
ID_CARD,
//座机号
FIXED_PHONE,
//手机号
MOBILE_PHONE,
//地址
ADDRESS,
//电子邮件
EMAIL,
//密码
PASSWORD,
//中国大陆车牌,包含普通车辆、新能源车辆
CAR_LICENSE,
//银行卡
BANK_CARD
}
自定义脱敏序列化器
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.DesensitizedUtil;
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 com.yiyan.study.annotation.Desensitization;
import com.yiyan.study.enums.DesensitizationTypeEnum;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;
/**
* @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 CUSTOM -> jsonGenerator.writeString(CharSequenceUtil.hide(String.valueOf(str), startInclude,
endExclude >= startInclude ? endExclude : str.length() + endExclude));
// userId脱敏
case USER_ID -> jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId()));
// 中文姓名脱敏
case CHINESE_NAME -> jsonGenerator.writeString(CharSequenceUtil.hide(String.valueOf(str), 1, str.length()));
// 身份证脱敏
case ID_CARD -> jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 1, 2));
// 固定电话脱敏
case FIXED_PHONE -> jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str)));
// 手机号脱敏
case MOBILE_PHONE -> jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
// 地址脱敏
case ADDRESS -> jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
// 邮箱脱敏
case EMAIL -> jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
// 密码脱敏
case PASSWORD -> jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
// 中国车牌脱敏
case CAR_LICENSE -> jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
// 银行卡脱敏
case BANK_CARD -> jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
}
}
@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);
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
if (desensitization != null) {
return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
desensitization.endExclude());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
自定义脱敏注解
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yiyan.study.enums.DesensitizationTypeEnum;
import com.yiyan.study.serialize.DesensitizationSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 脱敏注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
/**
* 脱敏数据类型,CUSTOM注解下,startInclude和endExclude生效
*/
DesensitizationTypeEnum type() default DesensitizationTypeEnum.CUSTOM;
/**
* 脱敏开始位置(包含)
*/
int startInclude() default 0;
/**
* 脱敏结束位置(不包含)
*/
int endExclude() default 0;
}
数据脱敏测试
定义测试对象
import com.yiyan.study.annotation.Desensitization;
import com.yiyan.study.enums.DesensitizationTypeEnum;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder
public class DesensitizationDTO implements Serializable {
@Desensitization(type = DesensitizationTypeEnum.CHINESE_NAME)
private String username;
@Desensitization(type = DesensitizationTypeEnum.EMAIL)
private String email;
@Desensitization(type = DesensitizationTypeEnum.ADDRESS)
private String address;
@Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE)
private String phoneNumber;
@Desensitization(type = DesensitizationTypeEnum.CUSTOM, startInclude = 1, endExclude = -2)
private String note;
}
测试接口
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yiyan.study.model.DesensitizationDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 启动类
*/
@SpringBootApplication
@Slf4j
@RestController
public class DesensitizationApplication {
private static final DesensitizationDTO dto = com.yiyan.study.model.DesensitizationDTO.builder()
.username("小李")
.phoneNumber("12300000456")
.email("li@gmail.com")
.address("XX.YY.DD.FF")
.note("123456789")
.build();
public static void main(String[] args) throws JsonProcessingException {
SpringApplication.run(DesensitizationApplication.class, args);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(dto);
log.info("Json : {}", s);
}
@GetMapping("/test")
public DesensitizationDTO desensitizationTest() {
return dto;
}
}