Node.js工程师养成计划内涵源码
download:http://www.51xuebc.com/thread-447-1-1.html
对接口返回的数据脱敏是如此简单。
背景
正要钓鱼的时候,突然看到产品慢慢过来,我就觉得不好了。他没说话,我说我做不到。我做不到。他转身回去了。过了一会儿,美妆产品经理慢慢来了。她没说话,我说什么都行。直接提出需求很简单。
就是一些接口返回的信息。如果涉及敏感数据,必须脱敏。我认为相反,这意味着什么。我会立即安排。
思考
要用可配置的多策略进行脱敏操作,否则脱敏操作会一个接口一个接口的进行,重复工作量太大,明显违背了程序员“多写一行算我输”的准则。
想一想,定义数据脱敏标注和数据脱敏逻辑的接口,将需要脱敏的属性添加到返回类中,并指定相应的脱敏策略操作。
接下来我只需要截取控制器返回的数据,找到带有脱敏注释的属性操作。一开始打算用@ControllerAdvice来实现,但是发现需要自己去reflection类获取注释。
当返回的对象比较复杂,需要递归来体现的时候,性能会一下子降低,所以我想到了通常的@JsonFormat,和我现在的场景很像。我可以通过自定义注释和字段解析器自定义字段解析。
实现代码
1.可以配置用户定义的数据注释和数据脱敏策略:
@Target({ElementType。字段,元素类型。类型})
@保留(RetentionPolicy。运行时间)
@已记录
公共@接口数据屏蔽{
DataMaskingFunc maskFunc()缺省值。NO _ MASK
}
复制代码
二:自定义序列化器,参考jackson的StringSerializer。下面的示例只降低了字符串类型的敏感度。
公共接口数据屏蔽操作{
String MASK _ CHAR = " *
字符串掩码(字符串内容,字符串mask char);
}
公共枚举数据掩码Func {
/**
*脱敏转换器
*/
NO_MASK((str,maskChar) -> {
返回字符串;
}),
ALL_MASK((str,maskChar) -> {
if(string utils . haslength(str)){
StringBuilder sb = new StringBuilder();
for(int I = 0;我
sb . append(string utils . haslength(mask char)?mask char:datamaskingpoperation。MASK _ CHAR);
}
return sb . tostring();
}否则{
返回字符串;
}
});
私有最终数据屏蔽操作操作;
私有DataMaskingFunc(datamaskingpoperation操作){
this.operation =操作;
}
公共数据屏蔽操作操作(){
返回this.operation
}
}
公共最终类DataMaskingSerializer扩展了StdScalarSerializer {
私有最终数据屏蔽操作操作;
公共datamaskinserializer(){
super(String.class,false);
this.operation = null
}
公共DataMaskingSerializer(datamaskingpoperation操作){
super(String.class,false);
this.operation =操作;
}
public boolean isEmpty(serializer provider prov,Object value) {
String str =(字符串)值;
返回str . isempty();
}
公共void serialize(对象值,JsonGenerator gen,SerializerProvider provider)引发IOException {
if (Objects.isNull(operation)) {
String content = DataMaskingFunc。ALL_MASK.operation()。mask((字符串)值,null);
gen.writeString(内容);
}否则{
String content = operation . mask((String)value,null);
gen.writeString(内容);
}
}
public final void serializeWithType(对象值、JsonGenerator gen、SerializerProvider、TypeSerializer typeSer)引发IOException {
this.serialize(value,gen,provider);
}
public JSON node get schema(serializer provider provider,typeHint) {
返回this . createschemdanode(" string ",true);
}
public void acceptjsonformattivisitor(JsonFormatVisitorWrapper visitor,JavaType typeHint)抛出JsonMappingException {
this.visitStringFormat(visitor,type hint);
}
}
复制代码
第三,定制AnnotationIntrospector,并修改我们定制的注释以返回相应的序列化程序。
@Slf4j
公共类DataMaskingAnnotationIntrospector扩展了NopAnnotationIntrospector {
@覆盖
公共对象findSerializer(带注释的am) {
data masking annotation = am . get annotation(data masking . class);
如果(注释!= null) {
返回新的DataMaskingSerializer(annotation . mask func()。operation());
}
返回null
}
}
复制代码
四:覆盖对象映射器:
@配置(
proxyBeanMethods = false
)
公共类数据掩码配置{
@配置(
proxyBeanMethods = false
)
@ conditional class({ Jackson 2 objectmapper builder . class })
静态类JacksonObjectMapperConfiguration {
JacksonObjectMapperConfiguration(){
}
@Bean
@主要
对象映射器jacksonObjectMapper(Jackson 2 ObjectMapper builder生成器){
object mapper object mapper = builder . createxml mapper(false)。build();
AnnotationIntrospector ai = object mapper . getserializationconfig()。getAnnotationIntrospector();
AnnotationIntrospector newAi = AnnotationIntrospector pair . pair(ai,new datamaskingantionintrospector());
object mapper . setannotationintrospector(newAi);
返回objectMapper
}
}
}
复制代码
五:带注释返回对象:
公共类用户实现可序列化{
/**
*主键ID
*/
私有长id;
/**
*姓名
*/
@ data masking(mask func = DataMaskingFunc。ALL_MASK)
私有字符串名称;
/**
*年龄
*/
私有整数年龄;
/**
*邮箱
*/
@ data masking(mask func = DataMaskingFunc。ALL_MASK)
私人字符串电子邮件;
}