在涉及到个人信息的业务接口中,数据脱敏是很重要的,那么如果最高效,最方便的做数据脱敏呢?
一般对便捷有要求的我们都可以通过注解配合spring Aop 或者相应的过滤器去实现
在项目中对json的处理我一般比较喜欢用阿里的fastjson , 我们只需要引入fastjson的依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
然后就是去继承webmvcconfiguer
@Component
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
/// -> JSON消息转换器(采用阿里的fastjson)
// 创建一个转换器对象;
FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
// 个性化配置转换特性
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 配置:要格式化返回的json数据
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// 配置:把空的值的key也返回
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);
// 字段如果为null,输出为false,而非null
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullBooleanAsFalse);
// 数值字段如果为null,输出为0,而非null
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullNumberAsZero);
// List字段如果为null,输出为[],而非null;
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullListAsEmpty);
// 字符类型字段如果为null,输出为"",而非null
fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteNullStringAsEmpty);
fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);
// 将convert添加到converters当中.
converters.add(1,fastJsonHttpMessageConverter);
}
}
这里要注意的是 因为我在mvc中组合了很多消息转换器 它们被房放在list集合中,当被返回值处理器调用的时候会按照顺序从第一个 消息转换器开始尝试,如果你没有将web starter中自引入的Jackson排除掉,那么第一要将fastjson的转换器添加到集合的前面,不然会失效。
将fastjson设为转换器后,我们写一个注解
@Documented
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Desensitive {
/**
* 数据脱敏策略(类型),默认无
*/
DesensitizedType value() default DesensitizedType.NONE;
}
public enum DesensitizedType {
/**
* 用户名
*/
USERNAME(s -> s.replaceAll("\\S*(\\S)", "***$1")),
/**
* 身份证
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
/**
* 手机号
*/
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 地址
*/
ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));
private final Function<String, String> desensitizer;
SensitiveEnum(Function<String, String> desensitizer) {
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer() {
return desensitizer;
}
}
然后写对应的过滤器逻辑 这个过滤器是fastjson在写入写入每个字段之前都会调用的过滤器
object 就是你要转为json的对象 name 就是当前字段的名称 value就是当前字段的值
如果是Jackson的话也有相应的过滤器
因为我们的枚举类是一个 函数式接口 因此我们在过滤器中判断如果加了当前注解,那么就得到当前注解枚举对应的 apply方法 将原有的value传进去进行脱敏即可 这里的脱敏可以自己实现也可以使用hutool中的工具类
public class DesensitizeValueFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
try {
Field field = object.getClass().getDeclaredField(name);
Desensitive desensitive = field.getAnnotation(Desensitive.class);
if (desensitive == null) {
return value;
}
if (!(value instanceof String valueStr) || ((String) value).length() == 0) {
return value;
}
String value= desensitive.value().apply(value);
return value
}
} catch (NoSuchFieldException e) {
return value;
}
}
最后别忘记了将写好的 ValueFilter 加入到 前面 fastjson的 config中去
这样 所有加了相应注解的字段便完成了脱敏 下次哪个字段想脱敏直接加相应的注解即可 是不是很方便呢