Java可通过自定义@Sensitive注解配合Jackson序列化器实现敏感字段自动脱敏:定义注解标记字段→序列化时反射识别→按枚举类型(PHONE/ID_CARD等)执行对应掩码逻辑→注册到ObjectMapper生效。

Java 中可以通过自定义注解 + 序列化框架(如 Jackson)的序列化器( JsonSerializer )或反射拦截,在序列化时自动对敏感字段脱敏。核心思路是:定义注解标记敏感字段 → 在序列化过程中通过反射识别该注解 → 动态替换字段值为脱敏后内容(如手机号中间四位变 *)。
1. 定义敏感字段注解
创建一个运行时保留的注解,用于标记需要脱敏的字段:
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Sensitive {
SensitiveType value() default SensitiveType.DEFAULT;
}配套枚举定义常见脱敏类型(可扩展):
public enum SensitiveType {
DEFAULT, // 默认规则:手机号、身份证等按通用逻辑
PHONE, // 手机号:138****1234
ID_CARD, // 身份证:11010119900101123<strong>X</strong> → 110101**********123X
EMAIL, // 邮箱:a***@b.com
BANK_CARD // 银行卡:6228**********1234
}2. 实现 Jackson 自定义序列化器
继承 JsonSerializer<object> </object>,在序列化每个对象前检查其字段是否含 @Sensitive,并根据类型执行对应脱敏逻辑:
public class SensitiveJsonSerializer extends JsonSerializer<object> {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value == null) {
gen.writeNull();
return;
}
<pre class="brush:php;toolbar:false;"> Class> clazz = value.getClass();
Map<string object> result = new LinkedHashMap();
// 遍历所有声明字段(含 private)
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Object fieldValue;
try {
fieldValue = field.get(value);
} catch (IllegalAccessException e) {
continue;
}
// 检查是否标记了 @Sensitive
Sensitive sensitive = field.getAnnotation(Sensitive.class);
if (sensitive != null) {
fieldValue = desensitize(fieldValue, sensitive.value());
}
result.put(field.getName(), fieldValue);
}
gen.writeStartObject();
for (Map.Entry<string object> entry : result.entrySet()) {
gen.writeStringField(entry.getKey(), Objects.toString(entry.getValue(), ""));
}
gen.writeEndObject();
}
private Object desensitize(Object raw, SensitiveType type) {
if (raw == null) return null;
String str = raw.toString();
return switch (type) {
case PHONE -> maskPhone(str);
case ID_CARD -> maskIdCard(str);
case EMAIL -> maskEmail(str);
case BANK_CARD -> maskBankCard(str);
default -> maskDefault(str);
};
}
private String maskPhone(String s) {
if (s.length() <p>}</p></string></string>3. 注册全局序列化器(Spring Boot 示例)
在 Spring Boot 中,通过配置 ObjectMapper 注册该序列化器,使其对所有对象生效(注意:需配合 @JsonSerialize(using = ...) 或全局配置):
推荐方式是使用模块注册,避免手动加注解:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
@Configuration
public class JacksonConfig {
<pre class="brush:php;toolbar:false;">@Bean
@Primary
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
// 对所有 Object 类型启用敏感字段序列化(谨慎!更推荐按类注册)
module.addSerializer(Object.class, new SensitiveJsonSerializer());
mapper.registerModule(module);
return mapper;
}}
⚠️ 注意:直接注册 Object.class 会影响所有对象,可能带来性能与兼容性问题。更稳妥的做法是:
- 让需要脱敏的 DTO 继承一个标记接口(如
Desensitizable),然后注册SimpleModule.addSerializer(Desensitizable.class, ...) - 或在具体 VO 类上显式标注:
@JsonSerialize(using = SensitiveJsonSerializer.class) - 也可结合 Jackson 的
BeanSerializerModifier,仅对含@Sensitive字段的类动态增强序列化逻辑(更精准但实现稍复杂)
4. 使用示例
在实体中使用注解:
public class UserVO {
private String name;
<pre class="brush:php;toolbar:false;">@Sensitive(SensitiveType.PHONE)
private String phone;
@Sensitive(SensitiveType.ID_CARD)
private String idCard;
@Sensitive
private String password; // 使用默认脱敏
// getter/setter...}
返回该对象时,Jackson 自动脱敏:
// 原始数据:phone="13812345678", idCard="11010119900101123X"
// 序列化后 JSON:
{
"name": "张三",
"phone": "138****5678",
"idCard": "110101**********123X",
"password": "pa***"
}不复杂但容易忽略:脱敏逻辑应与业务强相关,建议将 desensitize 方法抽成可配置的服务(如支持正则自定义、长度阈值、空值策略),并在测试中覆盖边界情况(null、空字符串、超短字符串等)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










