利用Jackson序列化实现数据脱敏
字数 1047 2025-08-11 08:36:16
利用Jackson序列化实现数据脱敏 - 详细教学文档
1. 背景与目标
1.1 背景
在项目中处理敏感信息(如客户手机号、身份证、车牌号等)时,直接展示存在隐私泄露风险,需要进行数据脱敏处理(用脱敏符号如*替换部分信息)。
1.2 目标
- 利用Jackson序列化机制实现数据脱敏
- 降低重复开发量,提升开发效率
- 形成统一有效的脱敏规则
- 支持可扩展、可自定义的个性化脱敏需求
2. 核心实现原理
2.1 Jackson序列化机制
通过自定义JsonSerializer实现脱敏逻辑,利用Jackson的注解系统绑定脱敏处理器。
3. 详细实现步骤
3.1 自定义脱敏序列化器
public class ObjectDesensitizeSerializer extends StdSerializer<Object> implements ContextualSerializer {
private transient Desensitization<Object> desensitization;
protected ObjectDesensitizeSerializer() {
super(Object.class);
}
@Override
public JsonSerializer<Object> createContextual(SerializerProvider prov, BeanProperty property) {
// 获取属性上的脱敏注解
Desensitize annotation = property.getAnnotation(Desensitize.class);
return createContextual(annotation.desensitization());
}
@SuppressWarnings("unchecked")
public JsonSerializer<Object> createContextual(Class<? extends Desensitization<?>> clazz) {
ObjectDesensitizeSerializer serializer = new ObjectDesensitizeSerializer();
if (clazz != DefaultDesensitization.class) {
serializer.setDesensitization((Desensitization<Object>) DesensitizationFactory.getDesensitization(clazz));
}
return serializer;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
Desensitization<Object> objectDesensitization = getDesensitization();
if (objectDesensitization != null) {
try {
gen.writeObject(objectDesensitization.desensitize(value));
} catch (Exception e) {
gen.writeObject(value);
}
} else if (value instanceof String) {
gen.writeString(Symbol.getSymbol(((String) value).length(), Symbol.STAR));
} else {
gen.writeObject(value);
}
}
}
关键点:
- 继承
StdSerializer并实现ContextualSerializer接口 createContextual方法获取字段注解信息并创建定制序列化器serialize方法执行实际脱敏逻辑
3.2 脱敏器接口与工厂
3.2.1 脱敏器接口
public interface Desensitization<T> {
T desensitize(T target);
}
3.2.2 脱敏器工厂
public class DesensitizationFactory {
private static final Map<Class<?>, Desensitization<?>> map = new HashMap<>();
public static Desensitization<?> getDesensitization(Class<?> clazz) {
if (clazz.isInterface()) {
throw new UnsupportedOperationException("desensitization is interface, what is expected is an implementation class !");
}
return map.computeIfAbsent(clazz, key -> {
try {
return (Desensitization<?>) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new UnsupportedOperationException(e.getMessage(), e);
}
});
}
}
3.3 常用脱敏器实现
3.3.1 默认脱敏实现
public interface DefaultDesensitization extends Desensitization<String> {}
3.3.2 手机号脱敏器
public class MobileNoDesensitization implements DefaultDesensitization {
private static final Pattern DEFAULT_PATTERN = Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}");
@Override
public String desensitize(String target) {
Matcher matcher = DEFAULT_PATTERN.matcher(target);
while (matcher.find()) {
String group = matcher.group();
target = target.replace(group,
group.substring(0, 3) +
Symbol.getSymbol(4, Symbol.STAR) +
group.substring(7, 11));
}
return target;
}
}
3.4 注解定义
3.4.1 基础脱敏注解
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = ObjectDesensitizeSerializer.class)
public @interface Desensitize {
Class<? extends Desensitization<?>> desensitization();
}
3.4.2 默认脱敏注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@Desensitize(desensitization = DefaultDesensitization.class)
public @interface DefaultDesensitize {}
3.4.3 手机号脱敏注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@Desensitize(desensitization = MobileNoDesensitization.class)
public @interface MobileNoDesensitize {}
3.5 脱敏符号工具类
public class Symbol {
public static final String STAR = "*";
public static String getSymbol(int number, String symbol) {
return IntStream.range(0, number)
.mapToObj(i -> symbol)
.collect(Collectors.joining());
}
}
4. 使用示例
4.1 实体类定义
public class User {
@MobileNoDesensitize
private String mobile;
@DefaultDesensitize
private String idCard;
// getters and setters
}
4.2 序列化结果
{
"mobile": "133****5678",
"idCard": "***************1234"
}
5. 执行流程剖析
- 调用
JsonUtil.toJsonString()开始序列化 - 识别属性上的脱敏注解(如
@MobileNoDesensitize) - 调用
ObjectDesensitizeSerializer#createContextual创建序列化器 - 调用对应的脱敏实现(如
MobileNoDesensitization#desensitize) - 输出脱敏后的序列化结果
6. 关键设计点
6.1 Jackson元注解机制
使用@JacksonAnnotationsInside实现组合注解,使Jackson能够识别自定义注解并应用相应的序列化逻辑。
6.2 扩展性设计
- 通过
Desensitization接口支持自定义脱敏规则 - 通过注解系统支持灵活配置
- 工厂模式管理脱敏器实例
7. 总结
本文详细介绍了利用Jackson序列化实现数据脱敏的完整方案,包括:
- 自定义序列化器的实现
- 脱敏器接口与工厂设计
- 常用脱敏规则实现
- 注解系统设计
- 执行流程与关键设计点
该方案具有高度可扩展性,可以方便地添加新的脱敏规则,同时保持代码的整洁和统一性。