Implementing Data Desensitization with Custom Jackson Annotations in Java
This article explains how to mask sensitive fields such as ID numbers, phone numbers, and addresses in Java APIs by creating a custom @Sensitive annotation, defining masking strategies, implementing a Jackson serializer, and testing the solution with a Spring REST controller, providing complete code examples and sample output.
In many API responses, sensitive data like ID numbers, phone numbers, and addresses must be masked; a common technique is to replace part of the value with asterisks.
The article presents two main ways to achieve data masking in Java, focusing on the Jackson‑based solution.
1. Define a custom Jackson annotation
Create a @Sensitive annotation that references a desensitization strategy.
/**
* 自定义jackson注解,标注在属性上
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {
//脱敏策略
SensitiveStrategy strategy();
}2. Define desensitization strategies
An enum SensitiveStrategy supplies lambda expressions for different masking rules (username, ID card, phone, address).
/**
* 脱敏策略,枚举类,针对不同的数据定制特定的策略
*/
public enum SensitiveStrategy {
/**
* 用户名
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 身份证
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
/**
* 手机号
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 地址
*/
ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));
private final Function
desensitizer;
SensitiveStrategy(Function
desensitizer) {
this.desensitizer = desensitizer;
}
public Function
desensitizer() {
return desensitizer;
}
}3. Implement JSON serializer
Implement SensitiveJsonSerializer extending JsonSerializer and ContextualSerializer to apply the selected strategy during serialization.
/**
* 序列化注解自定义实现
* JsonSerializer
:指定String 类型,serialize()方法用于将修改后的数据载入
*/
public class SensitiveJsonSerializer extends JsonSerializer
implements ContextualSerializer {
private SensitiveStrategy strategy;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(strategy.desensitizer().apply(value));
}
/**
* 获取属性上的注解属性
*/
@Override
public JsonSerializer
createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Sensitive annotation = property.getAnnotation(Sensitive.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
this.strategy = annotation.strategy();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
}4. Apply annotation to a model class
Annotate fields of the Person class with @Sensitive(strategy = …) to specify how each field should be masked.
@Data
public class Person {
/**
* 真实姓名
*/
@Sensitive(strategy = SensitiveStrategy.USERNAME)
private String realName;
/**
* 地址
*/
@Sensitive(strategy = SensitiveStrategy.ADDRESS)
private String address;
/**
* 电话号码
*/
@Sensitive(strategy = SensitiveStrategy.PHONE)
private String phoneNumber;
/**
* 身份证号码
*/
@Sensitive(strategy = SensitiveStrategy.ID_CARD)
private String idCard;
}5. Test via a REST controller
A simple Spring @RestController returns a Person instance; the response shows masked values such as "197****8206".
@RestController
public class TestController {
@GetMapping("/test")
public Person test() {
Person user = new Person();
user.setRealName("不才陈某");
user.setPhoneNumber("19796328206");
user.setAddress("浙江省杭州市....");
user.setIdCard("4333333333334334333");
return user;
}
}Calling the endpoint returns JSON with masked fields, for example:
{
"realName": "不*陈某",
"address": "浙江省****市..****",
"phoneNumber": "197****8206",
"idCard": "4333****34333"
}The article concludes that many masking techniques exist, and the optimal choice depends on project requirements and the desired elegance of the implementation.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.