数据脱敏系列
数据脱敏(Data Masking)是一种通过特定规则对敏感信息进行变形处理的技术,旨在保护隐私数据的安全。
接口数据脱敏是在前端显示敏感数据时,比如手机号、身份证号、邮箱等,将其部分字符替换为星号或其他符号,以保护用户隐私。
其中还可以穿插权限控制,有权限的用户可以看到明文数据,而没有权限的用户只能看到脱敏后的字符串。
实现
创建自定义注解
其中 @JsonSerialize
中配置自定义了序列化器
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializer.class)
public @interface Sensitive {
// 脱敏类型
SensitiveType type() default SensitiveType.DEFAULT;
// 前置保留位数(如手机号前3位)
int prefix() default -1;
// 后置保留位数(如身份证后4位)
int suffix() default -1;
// 替换字符
String mask() default "*";
}
定义脱敏类型
设置默认的脱敏规则
@RequiredArgsConstructor
@Getter
public enum SensitiveType {
// 默认,全部隐藏,显示****
DEFAULT(0, 0, "****"),
// 自定义,需要手动设置前置后置长度
CUSTOMER(-1, -1, "*"),
// 用户名, 只显示第一位,其它隐藏,比如:徐*
CHINESE_NAME(1, 0, "*"),
// 身份证号, 显示前六后四,其它隐藏,比如:110110********1234
ID_CARD(6, 4, "*"),
// 座机号,显示后四位,其它隐藏,比如:****1234
PHONE(0, 4, "*"),
// 手机号, 显示前三后四,其它隐藏,比如:176****1234
MOBILE(3, 4, "*"),
// 地址, 显示前六,其它隐藏,比如:北京市海淀区********
ADDRESS(6, 0, "******"),
// 电子邮件, @ 前显示第一位,其它隐藏,@ 后全显示,比如:s*****o@126.com
EMAIL(1, 0, "****"),
// 银行卡, 显示前六后四,其它隐藏,比如:622202************1234
BANK_CARD(6, 4, "*"),
// 密码, 永远是 ******, 与长度无关
PASSWORD(0, 0, "******"),
;
private final Integer prefix;
private final Integer suffix;
private final String mask;
}
编写脱敏方法
public class SensitiveUtils {
/**
* 邮箱
* 显示规则:{@code @} 前只显示一位,其它隐藏,{@code @} 后全部显示,示例:{@code d**@126.com}
*
* @param email 邮箱
* @return 脱敏字符串
*/
public static String email(String email, SensitiveType type) {
if (email == null) {
return null;
}
int index = StringUtils.indexOf(email, StringPool.AT);
if (index <= 1) {
return email;
}
String prefix = mask(StringUtils.substring(email, 0, index), type);
return prefix + email.substring(index);
}
public static String mask(String input, SensitiveType type) {
return mask(input, type.getPrefix(), type.getSuffix(), type.getMask());
}
public static String mask(String input, int prefix, int suffix, String mask) {
if (StringUtils.isEmpty(input)) {
return input;
}
boolean customMask = mask.length() > 1;
StringBuilder sb = new StringBuilder();
for (int i = 0, n = input.length(); i < n; i++) {
if (i < prefix) {
sb.append(input.charAt(i));
continue;
}
if (i > (n - suffix - 1)) {
sb.append(input.charAt(i));
continue;
}
if (customMask) {
if (i == prefix) {
sb.append(mask);
}
} else {
sb.append(mask);
}
}
return sb.toString();
}
}
创建自定义序列化类
@NoArgsConstructor
@AllArgsConstructor
public class SensitiveSerializer extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveType type;
private int prefix;
private int suffix;
private String mask;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(sensitive((String) value));
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
if (sensitive == null) {
sensitive = beanProperty.getContextAnnotation(Sensitive.class);
}
if (sensitive != null) {
return new SensitiveSerializer(sensitive.type(), sensitive.prefix(), sensitive.suffix(), sensitive.mask());
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
private String sensitive(String value) {
switch (type) {
case EMAIL: return SensitiveUtils.email(value, type);
case CUSTOMER: return SensitiveUtils.mask(value, prefix, suffix, mask);
default : return SensitiveUtils.mask(value, type);
}
}
}
使用
public class User {
private String name;
@Sensitive(type = SensitiveType.MOBILE)
private String mobile;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
@Sensitive(type = SensitiveType.EMAIL)
private String email;
}
测试
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
User user = new User();
user.setName("张三");
user.setMobile("13812345678");
user.setIdCard("123456789012345678");
user.setEmail("example@126.com");
return user;
}
}
访问输出如下
{
"name": "张三",
"mobile": "138****5678",
"idCard": "123456******5678",
"email": "e****@example.com"
}