How to Implement Field-Level Data Desensitization with Jackson in Java

This tutorial explains how to move data desensitization to the JSON serialization stage using Jackson, defines a @Sensitive annotation, custom strategies, a contextual serializer, and demonstrates the approach with a User class example that masks personal fields during serialization.

Programmer DD
Programmer DD
Programmer DD
How to Implement Field-Level Data Desensitization with Jackson in Java

Previously a MyBatis plugin was used for data desensitization, but it limited post‑query processing; moving the desensitization step to JSON serialization solves this problem.

Desensitization in Jackson Serialization

The transformation simply postpones desensitization to the JSON serialization phase using the Jackson library.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
    SensitiveStrategy strategy();
}

Desensitization strategies are defined as follows:

import java.util.function.Function;
/**
 * Desensitization strategies.
 */
public enum SensitiveStrategy {
    /** Username sensitive strategy. */
    USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
    /** ID card sensitive type. */
    ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
    /** Phone sensitive type. */
    PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
    /** Address sensitive type. */
    ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));

    private final Function<String, String> desensitizer;
    SensitiveStrategy(Function<String, String> desensitizer) {
        this.desensitizer = desensitizer;
    }
    public Function<String, String> desensitizer() {
        return desensitizer;
    }
}

Custom Desensitization Serializer

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;
import java.util.Objects;
/**
 * @author felord.cn
 * @since 1.0.8.RELEASE
 */
public class SensitiveJsonSerializer extends JsonSerializer<String> 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);
    }
}

The createContextual method obtains the @Sensitive annotation on a field and initializes the appropriate JsonSerializer; the serialize method applies the desensitization logic during JSON output.

Modified Sensitive Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {
    SensitiveStrategy strategy();
}

Usage Example

@Data
public class User {
    @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;
}
User user = new User();
user.setRealName("张三丰");
user.setPhoneNumber("13333333333");
user.setAddress("湖北省十堰市丹江口市武当山");
user.setIdCard("4333333333334334333");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
System.out.println(json);
{
    "realName":"张*丰",
    "address":"湖北省****市丹江口市武****",
    "phoneNumber":"133****3333",
    "idCard":"4333****34333"
}

The result shows successful field‑level masking; a switch could be added later to enable or disable desensitization based on context.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJSONCustom AnnotationJacksondata desensitization
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

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.