背景说明
现在的项目中需要对展示的数据进行脱敏处理,类似的场景很常见,比如说展示的手机号、银行卡、用户姓名等全部用***这类的特殊字符进行代替。我们的项目就需要将岗位展示列表中的用户岗位发布姓名全部用星号进行替换.最初的时候是入门级版本,几行代码就可以实现。后期对项目进行优化,考虑到脱敏的场景以及脱敏的形式可能会变化(比如说手机号脱敏要求前面三位后两位之外的进行脱敏,用户姓名要求全部脱敏处理),所以对原有逻辑进行了修改,于是有了进阶版实现方式,优点在于对脱敏业务与序列化处理进行解耦,便于拓展和后期维护,下面就具体说下处理过程,希望对有同样需求的同学有所帮助!
入门级处理
场景:将岗位查询列表中的用户姓名进行全部脱敏处理,使用三个星号进行代替.直接上代码:
实体类ShopPostInfo:
public class ShopPostInfo implements Serializable { private static final long serialVersionUID = -2040496864515327697L; // 数据脱敏处理,用户名全部替换为*** @JsonSerialize(using = DesensitizationSerializer.class) private String userName; // 省略其他信息
自定义脱敏处理器DesensitizationSerializer:
public class DesensitizationSerializer extends JsonSerializer<String> { // 序列化处理 @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 脱敏成*** gen.writeString("***"); } }
脱敏后截图:
可以看到用户的姓名都已经被处理成***了.到这里问题是解决了,但是后期再有其他的脱敏需求怎么办,比如说想对手机号进行脱敏处理,只想对前三位后两位之外的数字进行脱敏处理怎么办?重新定义一个手机号脱敏处理器?那以后需要维护的脱敏类会越来越多,显得会有所冗余,毕竟每种脱敏处理器中仅有脱敏策略不同,是否可以只专注于脱敏策略的实现,对于序列化的实现进行统一处理.基于上面的疑问就有了脱敏处理的进阶版,请往下看!
进阶版处理
自定义脱敏注解
@Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = DesensitizationSerializer.class) public @interface DesensitizationAnnotation { Class<? extends DesensitizationStrategy> strategy(); }
自定义脱敏注解序列化处理器
public class DesensitizationSerializer extends JsonSerializer<String> implements ContextualSerializer { // 脱敏处理策略 private DesensitizationStrategy desensitizationStrategy; public DesensitizationSerializer() { } public DesensitizationSerializer(DesensitizationStrategy desensitizationStrategy) { this.desensitizationStrategy = desensitizationStrategy; } // 序列化核心处理 @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { int a=0; // 脱敏处理公共逻辑 String desensitizationValue = desensitizationStrategy.desensitizationHandle(value); // 序列化处理公共方案 gen.writeString(desensitizationValue); } // 设置自定义脱敏策略:desensitizationSerializer @Override public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { JsonSerializer<?> desensitizationSerializer = null; if(null == property) desensitizationSerializer = prov.findNullValueSerializer(property); if(!Objects.equals(property.getType().getRawClass(), String.class)) desensitizationSerializer = prov.findValueSerializer(property.getType(), property); if(Objects.equals(property.getType().getRawClass(), String.class)){ // JsonSerializer设置自定义脱敏策略 desensitizationSerializer = handleDesensitizationSerializer(desensitizationSerializer, property); } return desensitizationSerializer; } // 脱敏设置 private JsonSerializer<?> handleDesensitizationSerializer(JsonSerializer<?> desensitizationSerializer, BeanProperty beanProperty) { // 获取脱敏注解 DesensitizationAnnotation desensitizationJsonSerializer = beanProperty.getAnnotation(DesensitizationAnnotation.class); if (desensitizationJsonSerializer == null) desensitizationJsonSerializer = beanProperty.getContextAnnotation(DesensitizationAnnotation.class); // 设置脱敏实例,添加自定义脱敏策略 if (desensitizationJsonSerializer != null) { try { desensitizationSerializer = new DesensitizationSerializer(desensitizationJsonSerializer.strategy().newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return desensitizationSerializer; } }
实体类中添加序列化处理器
public class ShopPostInfo implements Serializable { private static final long serialVersionUID = -2040496864515327697L; // modify by txm 2022/10/4 数据脱敏处理 // @JsonSerialize(using = DesensitizationSerializer.class) @DesensitizationAnnotation(strategy = FullSensitization.class) private String userName; // 省略其他信息 }
全部脱敏处理策略:
public class FullSensitization implements DesensitizationStrategy{ // 全脱敏处理 @Override public String desensitizationHandle(String oldValue) { return "***"; } }
指定格式脱敏策略,这里指定只对用户姓名第一个字以外进行脱敏处理(暂时没想出更合理的脱敏场景,能理解要优化的点就行)
public class PatternDeSensitization implements DesensitizationStrategy{ // 部分脱敏处理,首个字符处理,其余都脱敏.这里使用的hutool中的strUtil工具类,直接使用string原生的方法也可以. @Override public String desensitizationHandle(String oldValue) { return StrUtil.replace(oldValue,1,oldValue.length(),'*'); } }
测试结果:
全部脱敏实现效果:
部分脱敏实现效果:
以上是关于数据脱敏的实现方式,其中根据业务需求做了一下结构优化,如果有相同的业务需求,看完希望对你有帮助,欢迎评论区留言点赞!