接口统一脱敏处理

#接口统一脱敏处理

前言

最近 在做一个关于数据脱敏的需求,就是将接口返回的一些敏感信息用*代替,对用户的敏感数据予以保护,因为线上出现了用户账号申诉被盗的情况
其实这样一个需求,做起来是是蛮简单的,在前端交互支持的情况下,我们在接口数据序列化之前把它替换掉就可以了。但是因为它是一个相对通用的业务,所以考虑将它做到基础框架里面去。

思路

基本思路:拿到原始待序列化的对象,通过自定义注解的方式,对不同的字段进行相应的脱敏处理
https://github.com/DannyHoo/desensitized 这里前人有一个不错的思路,基本就是这样实现的,但是他使用的是ResponseBodyAdvice结合注解来处理接口和对象,所有的接口(包括非序列化的接口)都要走到这个逻辑里面,性能必定会受到一些影响,而且为了不对原始对象处理,采用了对象反射和深拷贝使得性能会大大降低。但是在并发不高的情况下作为一个全局的脱敏处理框架还是可行的。

扩展

但是我要应对的场景,并发还是挺高的,峰值tps目前在150左右,虽然不是每个接口的调用都会有大并发,但是既然要做成框架性的东西,就不能确定使用方的调用频率了,因此需要换一种更简单的思路来实现以提高性能。
对于springmvc来说,其实已经帮我们做了一次序列化操作,因此我们可以利用这个序列化过程,直接定制处理,不用手动再去处理一次,因此我使用com.fasterxml.jackson序列化来实现这个功能。
另外对于方法的控制,我使用拦截器和线程上下文来处理,被拦截要加密的接口会设置脱敏上下文,包括国际化信息等等,在处理结束后记得清理,以免造成线程污染。

show me the code

1
/**
2
 * @Description 脱敏注解
3
 * @Author Storm
4
 * @date 2020.11.25 17:00
5
 */
6
@Target({ElementType.FIELD})
7
@Retention(RetentionPolicy.RUNTIME)
8
@JacksonAnnotationsInside
9
@JsonSerialize(using = DesensitizedSerializer.class)
10
public @interface Desensitized {
11
12
    /*脱敏类型(规则)枚举*/
13
    SensitiveTypeEnum type();
14
}
1
/**
2
 * @Description 脱敏序列化器,根据属性上的注解和上下文信息进行脱敏,使用时注意上下文的设置和清理,避免线程污染!!
3
 * @Author Storm
4
 * @date 2020.11.25 17:10
5
 */
6
public class DesensitizedSerializer extends StdSerializer<String> implements ContextualSerializer {
7
8
    private static final long serialVersionUID = 1L;
9
10
    private SensitiveTypeEnum type;
11
12
    protected DesensitizedSerializer(Class<String> t) {
13
        super(t);
14
    }
15
16
    protected DesensitizedSerializer() {
17
        super(String.class);
18
    }
19
20
    protected DesensitizedSerializer(SensitiveTypeEnum type) {
21
        super(String.class);
22
        this.type = type;
23
    }
24
25
    @Override
26
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {
27
    //获取注解上下文,拿到配置的脱敏类型
28
        Desensitized annotation = beanProperty.getAnnotation(Desensitized.class);
29
        if (annotation == null) {
30
            return new DesensitizedSerializer();
31
        }
32
        SensitiveTypeEnum type = annotation.type();
33
        return new DesensitizedSerializer(type);
34
    }
35
36
    @Override
37
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
38
        DesensitizeContext context = DesensitizeContext.getContext();
39
        if (context == null || !context.getNeedDesensitize()) {
40
            //不需要进行脱敏处理
41
            jsonGenerator.writeString(s);
42
            return;
43
        }
44
        String language = UserContext.get().getHl();
45
        jsonGenerator.writeString(DesensitizedUtils.desensitize(type, s, language));//根据类型进行具体的脱敏处理,回写到对象中
46
    }
1
/**
2
 * @Description 脱敏上下文信息
3
 * @Author Storm
4
 * @date 2020.11.25 17:48
5
 */
6
public class DesensitizeContext {
7
    public static final ThreadLocal<DesensitizeContextObj> context = new ThreadLocal<>();
8
9
    @Data
10
    public static class DesensitizeContextObj {
11
        /**
12
         * 语言 zh en
13
         */
14
        private String language = Locale.ENGLISH.getLanguage();
15
16
        /**
17
         * 是否需要脱敏
18
         */
19
        private boolean needDesensitize = false;
20
21
        public DesensitizeContextObj() {
22
        }
23
24
        public DesensitizeContextObj(String language, boolean needDesensitize) {
25
            this.language = language;
26
            this.needDesensitize = needDesensitize;
27
        }
28
    }
29
30
31
    public static DesensitizeContextObj getContext() {
32
        return context.get();
33
    }
34
35
    public static void setContext(DesensitizeContextObj obj) {
36
        context.set(obj);
37
    }
38
39
    public static void removeContext() {
40
        if (context.get() == null) {
41
            return;
42
        }
43
        context.remove();
44
    }
45
}
1
public class DesensitizeInterceptor extends HandlerInterceptorAdapter {
2
3
    @Override
4
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
5
        //设置脱敏上下文
6
        DesensitizeContext.setContext(true);
7
        return true;
8
    }
9
10
    @Override
11
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
12
        //清除脱敏上下文
13
        DesensitizeContext.removeContext();
14
    }
15
}
1
    @Configuration
2
    class MvcConfiguration extends WebMvcConfiguration {
3
        @Override
4
        protected void addInterceptors(InterceptorRegistry registry) {
5
            super.addInterceptors(registry);
6
registry.addInterceptor(desensitizeInterceptor()).addPathPatterns("/**");
7
        }
8
9
    }
1
//使用
2
 @Desensitized(type = SensitiveTypeEnum.EMAIL)
3
  private String email;