这样写脱敏工具方法类才优雅-利用枚举和注解优雅脱敏

本文提供一个技术干货,只需要在需要脱敏的字段上加一个注解,轻松实现报文脱敏。学会之后,你在项目组的江湖地位将直线上升!
直接上干货。

定义枚举类

public enum DesensitizationStrategy {

    PHONE(str -> str.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
    EMAIL(str -> str.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1****$3$4")),
    ID_CARD(str -> str.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
    BANK_CARD(str -> str.replaceAll("(\\d{4})\\d{8}(\\d{4})", "$1****$2"));

    private final Function<String, String>  function;

    private DesensitizationStrategy(Function<String, String> function) {
        this.function = function;
    }

    public String apply(String str) {
        return function.apply(str);
    }

    public static void main(String[] args) {
        System.out.println(DesensitizationStrategy.PHONE.apply("12345678901"));
    }
}

仅仅只需要这个枚举类就可以当做一个脱敏工具类来用,可以关注上面的main方法“DesensitizationStrategy.PHONE.apply(“12345678901”)”,这就是实现了手机号脱敏。
这个枚举类的特别之处就是使用了函数式接口作为枚举的属性,学会这招能将很多工具类改为枚举类的形式,相当优雅。

这个枚举类只是开胃菜,优雅的是下面这个注解的写法。

定义注解

/**
 * 脱敏注解
 * 用于标记需要脱敏的字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerializer.class)
public @interface Desensitization {

    /**
     * 敏感数据类型。
     * @return 敏感数据类型枚举类
     */
    DesensitizationStrategy value() default DesensitizationStrategy.PHONE;
}

这个自定义注解的特别之处就是@JacksonAnnotationsInside,@JacksonAnnotationsInside是一个组合注解,主要标记在用户的自定义注解上,那么这个用户自定义注解上标记的所有其他注解也会生效。也就是说,我们的自定义注解上标记的注解–@JsonSerialize(using = DesensitizationSerializer.class)会和这个自定义注解一起生效。
用过jackson的都知道,@JsonSerialize(using = DesensitizationSerializer.class)这个注解的意思就是将被注解的字段的json序列化交给类“DesensitizationSerializer”实现,下面我们新建这个类:

序列化注解实现类

public class DesensitizationSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private Desensitization desensitization;

    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(desensitization.value().apply(s));

    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if (beanProperty != null) {
            // 说明有Desensitization注解,下面就是判断注解的类型,然后返回对应的序列化器
            // 这里只处理String类型的字段
            if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                // 获取字段上的注解
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                // 如果字段上没有注解,获取getter方法上的注解
                if (desensitization == null) {
                    // 获取getter方法上的注解
                    desensitization = beanProperty.getContextAnnotation(Desensitization.class);
                }
                if (desensitization != null) {
                    this.desensitization = desensitization;
                    return this;
                }
            }
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }
        return serializerProvider.findNullValueSerializer(null);
    }
}

这个类分别实现了ContextualSerializer 接口和继承了JsonSerializer父类,其实一看就懂,这里简单解释一下。

JsonSerializer类就是jackson提供的字段序列化方法的超类,我们可以实现它自定义序列化被注解的字段,比如上面提到的注解@JsonSerialize(using = DesensitizationSerializer.class),意思就是指定类DesensitizationSerializer为该字段的序列化方法类,被指定的这个类必须继承JsonSerializer超类。
ContextualSerializer接口是对JsonSerializer超类的加强,配合它一起使用。ContextualSerializer 的createContextual方法提供改字段的类型和注解,并能够根据特定的场景选择适合的序列化器,就像上面代码中所写的。并且,createContextual方法只会在第一次序列化字段时调用(因为字段的上下文信息在运行期不会改变),所以不用担心影响性能。

以上就是全部干货,使用方法如下:

使用方法

仅仅需要在你的响应题对象中,将需要脱敏的字段加上注解就好了。比如,我有个接口响应体是这个People类,将该类需要脱敏的字段加上Desensitization注解,并标记字段的脱敏类型。

@Data
public class People {

    private Integer id;

    private String name;

    private Integer sex;

    private Integer age;

    @Desensitization(value = DesensitizationStrategy.PHONE) // 隐藏手机号
    private String phone;

    @Desensitization(value = DesensitizationStrategy.EMAIL) // 隐藏邮箱
    private String email;

    @Desensitization(value = DesensitizationStrategy.ID_CARD) // 隐藏身份证号
    private String iDCard;

    private String sign;
}

写个Controller测试一下:

    @GetMapping("/get-people")
    public People getPeopleInfo() {
         People people = new People();
        people.setId(1);
        people.setName("张三");
        people.setSex(1);
        people.setAge(18);
        people.setPhone("18888888888");
        people.setEmail("147641545@Gmail.com");
        people.setIDCard("1234567891234567");
        people.setSign("Hello World");
        return people;
    }

运行结果

{
    "id": 1,
    "name": "张三",
    "sex": 1,
    "age": 18,
    "phone": "188****8888",
    "email": "1****5@Gmail.com",
    "iDCard": "1234****6789",
    "sign": "Hello World"
}
  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪力乌龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值