关于数据脱敏,Spring boot 有话说

2023-02-21  本文已影响0人  今日开发技巧分享

前言

在项目开发过程中,有一些特殊场景需要对敏感数据进行脱敏返回展示,比如用户的手机号、证件号、真实姓名、邮箱等,返回如:176****9331 之类的,怎么实现呢?

使用@JsonSerialize对数据进行格式化

首先,来看个简单的例子,数据库有个20位的Long 类型的ID,返回到前端之后,因为前端类型问题,会丢失精度,这个时候,我们就需要将Long 类型的数据格式化为String类型进行返回,代码如下:

@Data
public class Demo {

  /** 数据ID **/
  @JsonSerialize(using = StringSerializer.class)
  private Long id;
}

上面的代码,只需要在字段添加 @JsonSerialize(using = StringSerializer.class) 注解就可以完成Long 返回 String 的格式化效果。接下来,我们看看 StringSerializer 做了什么事情,源码:

public final class StringSerializer extends StdScalarSerializer<Object> {
    private static final long serialVersionUID = 1L;

    public StringSerializer() {
        super(String.class, false);
    }

    public boolean isEmpty(SerializerProvider prov, Object value) {
        String str = (String)value;
        return str.isEmpty();
    }
    // 主要关注这个方法,这个是序列化方法
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        // 将 object 直接转换成 String 返回
        gen.writeString((String)value);
    }

    public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
        gen.writeString((String)value);
    }

    public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
        return this.createSchemaNode("string", true);
    }

    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
        this.visitStringFormat(visitor, typeHint);
    }
}

其实,只需要关注 serialize 这个方法,其他方法都是对其进行的增强,我们先看看StringSerializer 的UML 关系图:

这个时候我们就会发现serialize() 其实是JsonSerializer 的抽象方法,来看看 JsonSerializer 源码:

public abstract class JsonSerializer<T> implements JsonFormatVisitable {
    public JsonSerializer() {
    }

    public JsonSerializer<T> unwrappingSerializer(NameTransformer unwrapper) {
        return this;
    }

    public JsonSerializer<T> replaceDelegatee(JsonSerializer<?> delegatee) {
        throw new UnsupportedOperationException();
    }

    public JsonSerializer<?> withFilterId(Object filterId) {
        return this;
    }
    // 序列化抽象方法
    public abstract void serialize(T var1, JsonGenerator var2, SerializerProvider var3) throws IOException;

    public void serializeWithType(T value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
        Class<?> clz = this.handledType();
        if (clz == null) {
            clz = value.getClass();
        }

        serializers.reportBadDefinition(clz, String.format("Type id handling not implemented for type %s (by serializer of type %s)", clz.getName(), this.getClass().getName()));
    }

    public Class<T> handledType() {
        return null;
    }

    /** @deprecated */
    @Deprecated
    public boolean isEmpty(T value) {
        return this.isEmpty((SerializerProvider)null, value);
    }

    public boolean isEmpty(SerializerProvider provider, T value) {
        return value == null;
    }

    public boolean usesObjectId() {
        return false;
    }

    public boolean isUnwrappingSerializer() {
        return false;
    }

    public JsonSerializer<?> getDelegatee() {
        return null;
    }

    public Iterator<PropertyWriter> properties() {
        return ClassUtil.emptyIterator();
    }

    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type) throws JsonMappingException {
        visitor.expectAnyFormat(type);
    }

    public abstract static class None extends JsonSerializer<Object> {
        public None() {
        }
    }
}

分析到这里,聪明的小伙伴是不是精神一震,我了个擦,那我是不是可以自己继承这个超级类,实现自己的格式化逻辑(比如:脱敏~!!!)!没错了,就是这样,下面我们继续看,怎么去实现数据脱敏。

对手机号进行脱敏处理

开始之前,推进一款开源工具框架,只要你能想到的,这个工具基本上都支持,而且还是国产,那就是: hutool

首先,引入依赖(spring boot 自行引入):

<dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool.version}</version>
</dependency>

继承JsonSerializer 并实现 serialize 抽象方法:

public class PhoneDesensitizationSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String phone, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        String phoneDesensitization = DesensitizedUtil.mobilePhone(phone);
        jsonGenerator.writeString(phoneDesensitization);
    }
}

最后,使用PhoneDesensitizationSerializer 对说几号进行脱敏:

public class UserInfo {

    // 省略其他字段
    @JsonSerialize(using = PhoneDesensitizationSerializer.class)
    private String phoneNumber;
    
    // 省略其他字段
}

完活,其他的脱敏思路都是一样的,无非就是copy 改一下tool 方法。

总结

在遇到一个问题时,一定要多想,因为我们能遇到,别人一定也会遇到,总会找到快速解决问题的办法。

上一篇 下一篇

猜你喜欢

热点阅读