How to Implement Configurable Data Masking in Spring Boot with Custom Annotations
This article explains a configurable multi‑strategy data‑masking solution for Spring Boot APIs, covering the design of a custom annotation, serializer, AnnotationIntrospector, ObjectMapper integration, and example usage on response objects.
In the afternoon, a product request required masking sensitive data returned by certain APIs, prompting a configurable multi‑strategy data masking solution.
Approach
1. Define a data‑masking annotation and an interface for masking logic, then annotate fields in response classes with the desired masking strategy.
2. Intercept controller responses, locate annotated fields, and apply masking. Instead of using @ControllerAdvice with reflection (which hurts performance), a custom annotation combined with a field serializer similar to @JsonFormat is used.
Code
1. Custom data‑masking annotation with configurable strategy
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
}2. Custom serializer for String fields
public interface DataMaskingOperation {
String MASK_CHAR = "*";
String mask(String content, String maskChar);
}
public enum DataMaskingFunc {
NO_MASK((str, maskChar) -> { return str; }),
ALL_MASK((str, maskChar) -> {
if (StringUtils.hasLength(str)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
sb.append(StringUtils.hasLength(maskChar) ? maskChar : DataMaskingOperation.MASK_CHAR);
}
return sb.toString();
} else {
return str;
}
});
private final DataMaskingOperation operation;
private DataMaskingFunc(DataMaskingOperation operation) { this.operation = operation; }
public DataMaskingOperation operation() { return this.operation; }
}
public final class DataMaskingSerializer extends StdScalarSerializer<Object> {
private final DataMaskingOperation operation;
public DataMaskingSerializer() { super(String.class, false); this.operation = null; }
public DataMaskingSerializer(DataMaskingOperation operation) { super(String.class, false); this.operation = operation; }
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (Objects.isNull(operation)) {
String content = DataMaskingFunc.ALL_MASK.operation().mask((String) value, null);
gen.writeString(content);
} else {
String content = operation.mask((String) value, null);
gen.writeString(content);
}
}
}3. AnnotationIntrospector to bind the annotation to the serializer
@Slf4j
public class DataMaskingAnnotationIntrospector extends NopAnnotationIntrospector {
@Override
public Object findSerializer(Annotated am) {
DataMasking annotation = am.getAnnotation(DataMasking.class);
if (annotation != null) {
return new DataMaskingSerializer(annotation.maskFunc().operation());
}
return null;
}
}4. Override ObjectMapper configuration
@Configuration(proxyBeanMethods = false)
public class DataMaskConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Jackson2ObjectMapperBuilder.class})
static class JacksonObjectMapperConfiguration {
@Bean
@Primary
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
objectMapper.setAnnotationIntrospector(newAi);
return objectMapper;
}
}
}5. Apply the annotation to response objects
public class User implements Serializable {
private Long id;
@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
private String name;
private Integer age;
@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
private String email;
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
