几天前使用了Jackson对数据的自定义序列化。突发灵感,利用此方法来简单实现接口返回数据脱敏,故写此文记录。
核心思想是利用Jackson的StdSerializer
,@JsonSerialize
,以及自己实现的数据脱敏过程。
使用效果如下:
首先在需要进行脱敏的VO字段上面标注相关脱敏注解
调用接口即可看到脱敏效果
实现过程如下:
1. 定义脱敏的过程实现
/***CreatedbyEalenXieon2021/9/2415:52*顶级的脱敏器*/publicinterfaceDesensitization<T>{/***脱敏实现**@paramtarget脱敏对象*@return脱敏返回结果*/Tdesensitize(Ttarget);}
比如具体的手机号脱敏器实现
importcom.github.Symbol;importjava.util.regex.Matcher;importjava.util.regex.Pattern;/***CreatedbyEalenXieon2021/9/2415:56*手机号脱敏器默认只保留前3位和后4位*/publicclassPhoneDesensitizationimplementsStringDesensitization{/***手机号正则*/privatestaticfinalPatternDEFAULT_PATTERN=Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}");/***手机号脱敏只保留前3位和后4位*/@OverridepublicStringdesensitize(Stringtarget){Matchermatcher=DEFAULT_PATTERN.matcher(target);while(matcher.find()){Stringgroup=matcher.group();target=target.replace(group,group.substring(0,3)+Symbol.getSymbol(4,Symbol.STAR)+group.substring(7,11));}returntarget;}}
2.定义脱敏注解,并指明了使用的序列化器,注解中声明了使用的脱敏器实现
packagecom.github.annotation;importcom.fasterxml.jackson.annotation.JacksonAnnotationsInside;importcom.fasterxml.jackson.databind.annotation.JsonSerialize;importcom.github.desensitization.Desensitization;importcom.github.serializer.ObjectDesensitizeSerializer;importjava.lang.annotation.*;/***CreatedbyEalenXieon2021/10/811:30*/@Target({ElementType.FIELD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInside@JsonSerialize(using=ObjectDesensitizeSerializer.class)@Documentedpublic@interfaceDesensitize{/***脱敏器实现*/@SuppressWarnings("all")Class<?extendsDesensitization<?>>desensitization();}
3. 实现定义的序列化器
importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.BeanProperty;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.SerializerProvider;importcom.fasterxml.jackson.databind.ser.ContextualSerializer;importcom.fasterxml.jackson.databind.ser.std.StdSerializer;importcom.github.Symbol;importcom.github.annotation.Desensitize;importcom.github.desensitization.Desensitization;importcom.github.desensitization.DesensitizationFactory;importcom.github.desensitization.StringDesensitization;importjava.io.IOException;/***CreatedbyEalenXieon2021/8/99:03*脱敏序列化器*/publicclassObjectDesensitizeSerializerextendsStdSerializer<Object>implementsContextualSerializer{privatetransientDesensitization<Object>desensitization;protectedObjectDesensitizeSerializer(){super(Object.class);}publicDesensitization<Object>getDesensitization(){returndesensitization;}publicvoidsetDesensitization(Desensitization<Object>desensitization){this.desensitization=desensitization;}@OverridepublicJsonSerializer<Object>createContextual(SerializerProviderprov,BeanPropertyproperty){Desensitizeannotation=property.getAnnotation(Desensitize.class);returncreateContextual(annotation.desensitization());}@SuppressWarnings("unchecked")publicJsonSerializer<Object>createContextual(Class<?extendsDesensitization<?>>clazz){ObjectDesensitizeSerializerserializer=newObjectDesensitizeSerializer();if(clazz!=StringDesensitization.class){serializer.setDesensitization((Desensitization<Object>)DesensitizationFactory.getDesensitization(clazz));}returnserializer;}@Overridepublicvoidserialize(Objectvalue,JsonGeneratorgen,SerializerProviderprovider)throwsIOException{Desensitization<Object>objectDesensitization=getDesensitization();if(objectDesensitization!=null){try{gen.writeObject(objectDesensitization.desensitize(value));}catch(Exceptione){gen.writeObject(value);}}elseif(valueinstanceofString){gen.writeString(Symbol.getSymbol(((String)value).length(),Symbol.STAR));}else{gen.writeObject(value);}}}
4.代码的设计说明
完整代码可见 : https://github.com/EalenXie/jackson-desensitize
另附 基于Logback的日志脱敏方案(笔者认为这可能是全网最简单快捷的)
原理是利用Logback的自定义日志转换器ClassicConverter
1. 自定义脱敏日志转换器
importch.qos.logback.classic.pattern.ClassicConverter;importch.qos.logback.classic.spi.ILoggingEvent;importcom.github.desensitization.EmailDesensitization;importcom.github.desensitization.IDCardDesensitization;importcom.github.desensitization.PhoneDesensitization;importcom.github.desensitization.StringDesensitization;importjava.util.ArrayList;importjava.util.List;/***@authorEalenXiecreateon2021/3/1810:07*此Converter提供支持日志脱敏*1.编写此LogbackDesensitizeConverter*2.正则脱敏手机号/邮箱/身份证*/publicclassLogbackDesensitizeConverterextendsClassicConverter{protectedstaticfinalList<StringDesensitization>DESENSITIZATION_LIST=newArrayList<>();static{//手机号脱敏DESENSITIZATION_LIST.add(newPhoneDesensitization());//邮箱脱敏DESENSITIZATION_LIST.add(newEmailDesensitization());//身份证脱敏DESENSITIZATION_LIST.add(newIDCardDesensitization());}@OverridepublicStringconvert(ILoggingEventevent){Stringcontent=event.getMessage();try{for(StringDesensitizationdesensitization:DESENSITIZATION_LIST){content=desensitization.desensitize(content);}}catch(Exceptione){//ig}returncontent;}}
2. 启动类为PatternLayout
的静态变量defaultConverterMap
新增此自定义转换器
importch.qos.logback.classic.PatternLayout;importcom.github.filter.LogbackDesensitizeConverter;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.client.discovery.EnableDiscoveryClient;/***@authorEalenXiecreateon2020/11/2414:16*/@EnableDiscoveryClient@SpringBootApplicationpublicclassApiGatewayApplication{publicstaticvoidmain(String[]args){//日志处理方案新增一个Logback的日志脱敏转换器PatternLayout.defaultConverterMap.put("m",LogbackDesensitizeConverter.class.getName());SpringApplication.run(ApiGatewayApplication.class,args);}}
启动后可以看到日志脱敏效果。