优雅的实现业务数据脱敏

       业务数据有很多用户敏感数据,比如手机号、身份证号、姓名,报表系统对这些数据不加以处理,很容易被别人下载,导致用户信息泄露。作为开发人员,就需要对手机号、身份证号、姓名进行脱敏处理。

最简单的方法:

数据在返回给前端之前,遍历数据,对手机号、身份证号、姓名进行脱敏处理,如果有N张报表,我们就需要在N个controller层或者service层去处理,如果脱敏规则改了,就需要改N个controller层或者service层。

升级版:

将脱敏处理规则封装成一个方法,N个controller层或者service层,都使用这个方法,后续改动只改这一个方法,但是这样对controller层或者service层的侵入太强了,不符合高内聚低耦合的设计思想。

优雅版:

只在对应的对象上,指定对应的脱敏规则处理方法,在返回给前端的时候,使用com.fasterxml.jackson.databind.JsonSerializer进行序列化脱敏

具体代码:

1、引用jackson-databind,在springboot,这个包不需要单独引用

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6.1</version>
        </dependency>

2、定义指定脱敏规则的注解

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationJsonSerializer.class)
public @interface Desensitization {

    Class<? extends BizDataDesensitization> value();

    int userLevel() default 1;
}

3、重写脱敏序列化方法

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

import java.io.IOException;
import java.util.Objects;

/**
 * 重写脱敏序列化方法
 * 
 * 主要是这两个方法
 * com.cn.dl.springbootdemo.tuomin.DesensitizationJsonSerializer#serialize(java.lang.String, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider)
 * com.cn.dl.springbootdemo.tuomin.DesensitizationJsonSerializer#createContextual(com.fasterxml.jackson.databind.SerializerProvider, com.fasterxml.jackson.databind.BeanProperty)
 */
public class DesensitizationJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private BizDataDesensitization desensitization;

    //org.springframework.http.converter.HttpMessageNotWritableException:
    //No converter found for return value of type: class com.cn.dl.springbootdemo.tuomin.User

    /**
     * 需要加上默认构造器,否则会抛出上面的异常
     */
    public DesensitizationJsonSerializer() {

    }

    public DesensitizationJsonSerializer(BizDataDesensitization desensitization) {
        this.desensitization = desensitization;
    }

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

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        if(Objects.isNull(beanProperty)){
            return serializerProvider.findNullValueSerializer(null);
        }

        if(! Objects.equals(beanProperty.getType().getRawClass(), String.class)){
            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }

        if(Objects.equals(beanProperty.getType().getRawClass(), String.class)){
            try {
                //字段上有Desensitization注解的构造特定的脱敏函数
                Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
                if (Objects.nonNull(desensitization)) {
                    return new DesensitizationJsonSerializer(desensitization.value().newInstance());
                }
            } catch (IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

4、处理脱敏字段接口

import com.fasterxml.jackson.core.JsonGenerator;


@FunctionalInterface
public interface BizDataDesensitization {

    /**
     * 处理脱敏字段接口
     *
     * @param value
     * @param jsonGenerator
     * @return
     */
    String serialize(String value, JsonGenerator jsonGenerator);

}

5、身份证号脱敏规则

import com.fasterxml.jackson.core.JsonGenerator;

import java.util.Objects;

public class IdCardDesensitization implements BizDataDesensitization {

    /**
     * 身份证号脱敏
     *
     * 规则:只保留前三位、后四位
     *
     * @param idCardNo
     * @param jsonGenerator
     * @return
     */
    @Override
    public String serialize(String idCardNo, JsonGenerator jsonGenerator) {
        if(Objects.isNull(idCardNo)){
            return null;
        }
        return idCardNo.replaceAll("(?<=\\w{3})\\w(?=\\w{4})", "*");
    }

}

6、手机号脱敏规则

import com.fasterxml.jackson.core.JsonGenerator;
import org.springframework.util.StringUtils;

public class MobileDesensitization implements BizDataDesensitization {

    /**
     * 手机号脱敏
     *
     * 规则:只保留前三位,后四位
     *
     * @param mobile
     * @param jsonGenerator
     * @return
     */
    @Override
    public String serialize(String mobile, JsonGenerator jsonGenerator) {
        if (StringUtils.isEmpty(mobile) || (mobile.length() != 11)) {
            return mobile;
        }
        return mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
    }

}

7、User 测试Bean

import java.io.Serializable;

/**
 * User 测试Bean
 */
public class User implements Serializable {

    private static final long serialVersionUID = -3541789110487459884L;

    @Desensitization(IdCardDesensitization.class)
    private String idCardNo;

    @Desensitization(MobileDesensitization.class)
    private String mobile;

    private String address;

    private Integer age;

    public String getIdCardNo() {
        return idCardNo;
    }

    public void setIdCardNo(String idCardNo) {
        this.idCardNo = idCardNo;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

8、测试controller

@GetMapping(value = "/tuomin/demo")
    public User studentInfoV1(){
        try {
            User user = new User();
            user.setMobile("15799996666");
            user.setIdCardNo("122833199808098989");
            user.setAge(24);
            user.setAddress("beijing");
            return user;
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

9、测试结果

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

燕少༒江湖

给我一份鼓励!谢谢!

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

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

打赏作者

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

抵扣说明:

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

余额充值