使用jackson进行数据脱敏

背景

前两天看到有人写了个jackson的数据脱敏,突然挺感兴趣的,感觉以后的开发过程中也能够用到,就照着抄了一下,顺便自己消化了一下。

目的

在服务端返回数据时,利用Jackson序列化完成数据脱敏,达到对敏感信息脱敏展示。
降低重复开发量,提升开发效率
形成统一有效的脱敏规则
可基于重写默认脱敏实现的desensitize方法,实现可扩展、可自定义的个性化业务场景的脱敏需求

参考链接

地址

前期准备

StdSerializer:所有标准序列化程序所使用的基类,这个是编写自定义序列化程序所推荐使用的基类,这边用到它的serialize方法,来做序列化转化
ContextualSerializer:是Jackson 提供的另一个序列化相关的接口,它的作用是通过字段已知的上下文信息定制JsonSerializer,调用的createContextual方法,简单来讲就是通过它注入新增的序列化对象

实现思路

1.我们首先定义一个脱敏器接口****Desensitization,定义一个脱敏方法T desensitize(T target)
2.给脱敏接口一个默认的接口实现DefaultDesensitization,不定义方法,他也是一个接口,不做实现。
3.定义一个手机号脱敏实现类MobileNoDesensitization,实现DefaultDesensitization,实现脱敏方法desensitize,做自己的脱敏处理
4.定义一个脱敏注解Desensitize,定义需要的属性,脱敏实现类desensitization
5.定义默认的脱敏注解DefaultDesensitize,在这个注解上增加脱敏注解Desensitize,并指定脱敏实现类,这里是DefaultDesensitization
6.定义手机号脱敏注解MobileNoDesensitize,在这个注解上增加脱敏注解Desensitize,并指定脱敏实现类,这里是MobileNoDesensitization
7.定义脱敏序列化器,createContextual方法通过它注入新增的序列化对象。serialize方法,来做序列化转化
8.定义脱敏工厂,根据字节码,创建对象。
9.定义脱敏符号Symbol,做替换

代码例子

1.定义一个脱敏器接口****Desensitization

/**
 * 脱敏器
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 10:56
 */
public interface Desensitization<T> {
    /**
     * 脱敏实现
     *
     * @param target 脱敏对象
     * @return 脱敏返回结果
     */
    T desensitize(T target);
}

2.接口实现DefaultDesensitization

点击查看代码

/**
 * 默认脱敏实现
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 11:01
 */
public interface DefaultDesensitization extends Desensitization<String> {
}

3.手机号脱敏实现类MobileNoDesensitization

点击查看代码

/**
 * 手机号脱敏器,保留前3位和后4位
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 11:02
 */
public class MobileNoDesensitization implements DefaultDesensitization {
    /**
     * 手机号正则
     */
    private static final Pattern DEFAULT_PATTERN = Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}");


    /**
     * 手机号脱敏实现
     * @param target 脱敏对象
     * @return
     */
    @Override
    public String desensitize(String target) {
        Matcher matcher = DEFAULT_PATTERN.matcher(target);
        while (matcher.find()) {
            String group = matcher.group();
            target = target.replace(group, group.substring(0, 3) + Symbol.getSymbol(4, Symbol.STAR) + group.substring(7, 11));
        }
        return target;
    }
}

4.定义一个脱敏注解Desensitize

点击查看代码


/**
 * 脱敏注解
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 11:09
 */
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})//字段、枚举的常量,注解
@Retention(RetentionPolicy.RUNTIME)//运行时执行
@JacksonAnnotationsInside//组合注解,自定义注解需要
@JsonSerialize(using = ObjectDesensitizeSerializer.class)//指定序列化实现对象
@Documented
public @interface Desensitize {
    /**
     * 对象脱敏器实现
     */
    @SuppressWarnings("all")
    Class<? extends Desensitization<?>> desensitization();
}

5.定义默认的脱敏注解DefaultDesensitize

点击查看代码

/**
 * 默认脱敏注解
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 11:14
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@Desensitize(desensitization = DefaultDesensitization.class)
@Documented
public @interface DefaultDesensitize {

}

6.定义手机号脱敏注解MobileNoDesensitize

/**

  • 手机号脱敏注解
  • @author zhangxiaoxu15
  • @date 2022/2/8 11:18
    */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @Desensitize(desensitization = MobileNoDesensitization.class)
    @Documented
    public @interface MobileNoDesensitize {
    }

7.定义脱敏序列化器,createContextual方法通过它注入新增的序列化对象。serialize方法,来做序列化转化

点击查看代码



/**
 * 脱敏序列化器
 *
 * StdSerializer:所有标准序列化程序所使用的基类,这个是编写自定义序列化程序所推荐使用的基类。
 * ContextualSerializer:是Jackson 提供的另一个序列化相关的接口,它的作用是通过字段已知的上下文信息定制JsonSerializer。
 * @author zhangxiaoxu15
 * @date 2022/2/8 11:10
 */
public class ObjectDesensitizeSerializer extends StdSerializer<Object> implements ContextualSerializer {
    private static final long serialVersionUID = -7868746622368564541L;
    private transient Desensitization<Object> desensitization;

    protected ObjectDesensitizeSerializer() {
        super(Object.class);
    }

    public Desensitization<Object> getDesensitization() {
        return desensitization;
    }

    public void setDesensitization(Desensitization<Object> desensitization) {
        this.desensitization = desensitization;
    }

    /**
     * 扫描到的进入序列化
     * createContextual可以获得字段的类型以及注解。当字段拥有自定义注解时,取出注解中的值创建定制的序列化方式,这样在serialize方法中便可以得到这个值了。
     * createContextual方法只会在第一次序列化字段时调用(因为字段的上下文信息在运行期不会改变),所以无需关心性能问题。
     * @param prov
     * @param property
     * @return
     */
    @Override
    public JsonSerializer<Object> createContextual(SerializerProvider prov, BeanProperty property) {
//获取属性注解
        Desensitize annotation = property.getAnnotation(Desensitize.class);
        return createContextual(annotation.desensitization());
    }

    @SuppressWarnings("unchecked")
    public JsonSerializer<Object> createContextual(Class<? extends Desensitization<?>> clazz) {
        ObjectDesensitizeSerializer serializer = new ObjectDesensitizeSerializer();
        if (clazz != DefaultDesensitization.class) {
            serializer.setDesensitization((Desensitization<Object>) DesensitizationFactory.getDesensitization(clazz));
        }
        return serializer;
    }

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        Desensitization<Object> objectDesensitization = getDesensitization();
        if (objectDesensitization != null) {
            try {
                gen.writeObject(objectDesensitization.desensitize(value));
            } catch (Exception e) {
                gen.writeObject(value);
            }
        } else if (value instanceof String) {
            gen.writeString(Symbol.getSymbol(((String) value).length(), Symbol.STAR));
        } else {
            gen.writeObject(value);
        }
    }
}

8.定义脱敏工厂,根据字节码,创建对象。

点击查看代码

/**
 * 工厂方法
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 10:58
 */
public class DesensitizationFactory {
    private DesensitizationFactory() {
    }

    private static final Map<Class<?>, Desensitization<?>> map = new HashMap<>();


    @SuppressWarnings("all")
    /**
     * 通过clazz求class对象
     */
    public static Desensitization<?> getDesensitization(Class<?> clazz) {
        if (clazz.isInterface()) {
            throw new UnsupportedOperationException("desensitization is interface, what is expected is an implementation class !");
        }
        return map.computeIfAbsent(clazz, key -> {
            try {
                return (Desensitization<?>) clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new UnsupportedOperationException(e.getMessage(), e);
            }
        });
    }
}

9.定义脱敏符号

点击查看代码

/**
 * 脱敏符号
 *
 * @author zhangxiaoxu15
 * @date 2022/2/8 10:53
 */
public class Symbol {
    /**
     * '*'脱敏符
     */
    public static final String STAR = "*";

    private Symbol() {
    }

    /**
     * 获取符号
     *
     * @param number 符号个数
     * @param symbol 符号
     */
    public static String getSymbol(int number, String symbol) {
        return IntStream.range(0, number).mapToObj(i -> symbol).collect(Collectors.joining());
    }
}

10.定义java bean

点击查看代码

public class Person {
    @MobileNoDesensitize
    private String moblie;
    @DefaultDesensitize
    private String name;

    public String getMoblie() {
        return moblie;
    }

    public void setMoblie(String moblie) {
        this.moblie = moblie;
    }

    public Person(String moblie, String name) {
        this.moblie = moblie;
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

执行情况

点击查看代码

@SpringBootTest
class DemoTestApplicationTests {


    /**
     * 首先定义脱敏序列化实现类ObjectDesensitizeSerializer,主要作用是获取序列化对象和实现类
     * 定义注解@Desensitize,指定序列化实现对象。绑定脱敏实现类
     * 定义手机号脱敏注解@MobileNoDesensitize,绑定脱敏实现类
     * @throws JsonProcessingException
     */
      @Test
    void contextLoads() throws JsonProcessingException {
          Person person=new Person("15927388235","12121");
          ObjectMapper objectMapper = new ObjectMapper();

          //将对象序列化为json字符串
          objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //忽略为null的字段
          String userJsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(person);
          System.out.println(userJsonString);
        System.out.println("11111111111");
    }

}

点击查看代码
{
  "moblie" : "159****8235",
  "name" : "*****"
}
posted @ 2023-04-11 20:14  小陈子博客  阅读(1132)  评论(0编辑  收藏  举报