背景
Spring 将响应数据转换为前端 JSON 字符串时,有一些特殊需求,例如数据库中时间字段为 Long 类型,转换为 JSON 需要是时间字符串,有些敏感数据的信息隐藏等。
本文介绍一个简单的 Jackson 注解,实现对某些字段的信息隐藏,即将目标对象中的属性,用对应无实际意义的字符替换。
定义 Jackson 注解
使用 @JacksonAnnotationsInside
,定义一个在字段和方法上的注解类 HideSensitiveInfo
,包含掩码和长度两个方法:
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD,ElementType.METHOD})@JacksonAnnotationsInside@JsonSerialize(using=HideSensitiveInfoSerializer.class)public@interfaceHideSensitiveInfo{/***掩盖数据的字符串为什么,默认为**@return*/Stringmask()default"*";/***使用多少个掩盖字符串*@return*/intcount()default5;}
定义注解对应的序列化类
使用该注解的字段或者方法,序列化使用的类 HideSensitiveInfoSerializer
定义, 序列化类与注解是一对一的关系,注解定义了基础配置信息,序列化类获取对应的属性完成 JSON 序列化的输出过程。
publicclassHideSensitiveInfoSerializerextendsStdSerializer<String>implementsContextualSerializer{privateStringmask="?";privateintcount=1;/***构造函数:必须提供默认构造函数,调用父类的构造函数,且提供一个T的类型*/publicHideSensitiveInfoSerializer(){super(String.class);}/***有参构造函数,第一步必须调用默认构造函数*@parammask*@paramcount*/publicHideSensitiveInfoSerializer(Stringmask,intcount){this();this.mask=mask;this.count=count;}@OverridepublicJsonSerializer<?>createContextual(SerializerProviderserializerProvider,BeanPropertybeanProperty)throwsJsonMappingException{Stringmask="?";intcount=1;HideSensitiveInfoanno=null;if(beanProperty!=null){anno=beanProperty.getAnnotation(HideSensitiveInfo.class);}if(anno!=null){mask=anno.mask();count=anno.count();}returnnewHideSensitiveInfoSerializer(mask,count);}@Overridepublicvoidserialize(Strings,JsonGeneratorjsonGenerator,SerializerProviderserializerProvider)throwsIOException{Stringcontent="";for(inti=0;i<count;i++){content+=mask;}jsonGenerator.writeString(content);}}
序列化具有 @HideSensitiveInfo
注解的字段时,简单的用掩码字符拼成一个制定长度的字符串即可。
Java 实体引用注解
定义一个 Java 实体,对需要脱敏的数据使用 @HideSensitiveInfo
注解:
DatapublicclassMyData{@HideSensitiveInfo(mask="-",count=7)privateStringtelephone;@HideSensitiveInfo(mask="*",count=3)privateStringname;publicMyData(Stringtelephone,Stringname){this.telephone=telephone;this.name=name;}}
测试 Controller 类
@RestController@RequestMapping("/test")publicclassMyTestJson{@ResponseBody@RequestMapping("/hello")publicMyDatahello(){returnnewMyData("13220045018","10");}}
访问该接口,得到隐藏后的信息:
启示录
Java 的注解相比类传递信息更灵活,一行注解就可以额外设置配置信息,省却了类的 new
及属性设置动作。但是注解一般不能单独使用,必须绑定到其他使用它的类上完成对应的功能。