Information Security 6 min read

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.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Implementing Data Desensitization with Custom Jackson Annotations in Java

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.

backendJavaSpringannotationJacksonData Desensitization
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.