自定义一个 Jackson 转换注解完成敏感数据的隐藏

Posted 毕小宝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义一个 Jackson 转换注解完成敏感数据的隐藏相关的知识,希望对你有一定的参考价值。

背景

Spring 将响应数据转换为前端 JSON 字符串时,有一些特殊需求,例如数据库中时间字段为 Long 类型,转换为 JSON 需要是时间字符串,有些敏感数据的信息隐藏等。

本文介绍一个简单的 Jackson 注解,实现对某些字段的信息隐藏,即将目标对象中的属性,用对应无实际意义的字符替换。

定义 Jackson 注解

使用 @JacksonAnnotationsInside ,定义一个在字段和方法上的注解类 HideSensitiveInfo,包含掩码和长度两个方法:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@JacksonAnnotationsInside
@JsonSerialize(using = HideSensitiveInfoSerializer.class)
public @interface HideSensitiveInfo {
    /**
     * 掩盖数据的字符串为什么,默认为 *
     * @return
     */
    String mask() default "*";

    /**
     * 使用多少个掩盖字符串
     * @return
     */
    int count() default 5;
}

定义注解对应的序列化类

使用该注解的字段或者方法,序列化使用的类 HideSensitiveInfoSerializer 定义,
序列化类与注解是一对一的关系,注解定义了基础配置信息,序列化类获取对应的属性完成 JSON 序列化的输出过程。

public class HideSensitiveInfoSerializer extends StdSerializer<String> implements ContextualSerializer {
    private String mask = "?";

    private int count = 1;

    /**
     * 构造函数:必须提供默认构造函数,调用父类的构造函数,且提供一个 T 的类型
     */
    public HideSensitiveInfoSerializer() {
        super(String.class);
    }

    /**
     * 有参构造函数,第一步必须调用默认构造函数
     * @param mask
     * @param count
     */
    public HideSensitiveInfoSerializer(String mask, int count) {
        this();
        this.mask = mask;
        this.count = count;
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        String mask = "?";
        int count = 1;

        HideSensitiveInfo anno = null;
        if(beanProperty != null) {
            anno = beanProperty.getAnnotation(HideSensitiveInfo.class);
        }

        if (anno !=null ) {
            mask = anno.mask();
            count = anno.count();
        }

        return new HideSensitiveInfoSerializer(mask,count);
    }

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        String content = "";
        for (int i=0;i<count;i++) {
            content += mask;
        }

        jsonGenerator.writeString(content);
    }
}

序列化具有 @HideSensitiveInfo 注解的字段时,简单的用掩码字符拼成一个制定长度的字符串即可。

Java 实体引用注解

定义一个 Java 实体,对需要脱敏的数据使用 @HideSensitiveInfo 注解:

Data
public class MyData {

    @HideSensitiveInfo(mask="-",count = 7)
    private String telephone;

    @HideSensitiveInfo(mask="*",count = 3)
    private String name;

    public MyData(String telephone,String name) {
        this.telephone = telephone;
        this.name = name;
    }
}

测试 Controller 类

@RestController
@RequestMapping("/test")
public class MyTestJson {

    @ResponseBody
    @RequestMapping("/hello")
    public MyData hello() {
        return new MyData("13220045018","10");
    }
}

访问该接口,得到隐藏后的信息:

启示录

Java 的注解相比类传递信息更灵活,一行注解就可以额外设置配置信息,省却了类的 new 及属性设置动作。但是注解一般不能单独使用,必须绑定到其他使用它的类上完成对应的功能。

以上是关于自定义一个 Jackson 转换注解完成敏感数据的隐藏的主要内容,如果未能解决你的问题,请参考以下文章

利用Jackson序列化实现数据脱敏

Jackson注解自定义数据脱敏策略

Jackson注解自定义数据脱敏策略

json之jackson的常用全局配置以及自定义序列化规则

Jackson总结:常用注解整合spring自定义JsonSerializer

JAVA数据脱敏