美文网首页
关于数据脱敏,Spring boot 有话说

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

作者: 今日开发技巧分享 | 来源:发表于2023-02-21 11:19 被阅读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 方法。

    总结

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

    相关文章

      网友评论

          本文标题:关于数据脱敏,Spring boot 有话说

          本文链接:https://www.haomeiwen.com/subject/exinkdtx.html