SpringBoot实战:SpringBoot 数据脱敏

一、什么是数据脱敏

数据脱敏,意思是对数据进行去隐私化或者隐藏数据、数据变形,是一种数据手段,对于某些敏感信息,例如身份证号,银行卡号,手机号,家庭住址等 通过特定的敏感规则进行数据隐藏或者变形,从而实现敏感隐私数据的可靠保护。这样可以在开发和测试阶段或者其他非生成的环境中,有效提高敏感数据的隐私性。


二、敏感数据的主要应用场景

数据脱敏技术主要应用于处理包含客户安全信息及商业敏感数据的场景,诸如身份证号、手机号码、银行卡号、客户编号等个人敏感信息均需经过脱敏处理。其核心作用在于,借助专业的数据脱敏产品,确保企业内部对隐私数据的访问与使用受到合理限制,防范未经脱敏的隐私数据泄露至企业外部,从而平衡企业保护用户隐私数据与遵守监管合规要求的双重需求。

三、代码示例

@JsonSerialize:

@JsonSerialize 注解在Jackson库中扮演着关键角色,它能够在字段、方法或类级别上应用,为用户提供了高度自定义的序列化机制。通过此注解,开发者能够精细控制Java对象到JSON数据的转换过程。具体而言,@JsonSerialize允许我们指定一个自定义的序列化器类,通过using属性来实现。这个自定义序列化器需要继承自JsonSerializer<T>,其中T是待序列化对象的类型,使得开发者能够编写特定的逻辑来决定如何将对象状态映射到JSON格式。
除了using属性外,@JsonSerialize还包含其他有用的属性,如include,它用于定义序列化的包含策略。通过设置include属性,开发者可以指示Jackson在何种条件下将字段或属性包含进JSON输出中,比如仅当字段非空时才进行序列化。这样的设计不仅增强了序列化的灵活性,还帮助减少了JSON数据中的冗余信息,使得输出更加紧凑和高效。

接下来写一个简单例子,大家看一下

@Data
public class CustomDateClass {
  
    @JsonSerialize(using = CustomDateSerializer.class)
    private Date myDate;
  
    //自定义的日期序列化器
    public static class CustomDateSerializer extends ToStringSerializer<Date> {
        private static final long serialVersionUID = 1L;
  
        public CustomDateSerializer() {
          //传递null以使用默认日期格式
            super(null);
        }
 
        //重写 serialize 方法来定制日期的序列化过程,将其格式化为 "yyyy-MM-dd" 的字符串。
        @Override  
        public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            String formattedDate = new SimpleDateFormat("yyyy-MM-dd").format(value);
            jgen.writeString(formattedDate);
        }
    }
}

定义Jackson注解

自定义一个用于脱敏的注解,其标注的方法则会执行脱敏操作

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {
    //脱敏策略
    SensitiveStrategy strategy();
}

脱敏策略

/**
 * 脱敏策略,枚举类,针对不同的数据定制特定的策略
 */

public enum SensitiveStrategy
{
    /**
     * 姓名,第2位星号替换
     */

    USERNAME(s -> s.replaceAll("(\S)\S(\S*)", "$1*$2")),

    /**
     * 密码,全部字符都用*代替
     */

    PASSWORD(DesensitizedUtil::password),

    /**
     * 身份证,中间10位星号替换
     */

    ID_CARD(s -> s.replaceAll("(\d{4})\d{10}(\d{4})", "$1** **** ****$2")),

    /**
     * 手机号,中间4位星号替换
     */

    PHONE(s -> s.replaceAll("(\d{3})\d{4}(\d{4})", "$1****$2")),

    /**
     * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换
     */

    EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")),

    /**
     * 银行卡号,保留最后4位,其他星号替换
     */

    BANK_CARD(s -> s.replaceAll("\d{15}(\d{3})", "**** **** **** **** $1")),

    /**
     * 车牌号码,包含普通车辆、新能源车辆
     */

    CAR_LICENSE(DesensitizedUtil::carLicense);

    private final Function<String, String> desensitizer;

    SensitiveStrategy(Function<String, String> desensitizer)
    {
        this.desensitizer = desensitizer;
    }

    public Function<String, String> desensitizer()
    {
        return desensitizer;
    }
}

JSON序列化实现

对标注注解@Sensitive的字段进行脱敏

/**
 * 序列化注解自定义实现
 * JsonSerializer<String>:指定String 类型,serialize()方法用于将修改后的数据载入
 */

public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {

    private SensitiveStrategy strategy;
 
    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeString(strategy.desensitizer().apply(value));
    }
 
    /**
     * 获取属性上的注解属性
     */

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
 
        Sensitive annotation = property.getAnnotation(Sensitive.class);
        if (Objects.nonNull(annotation)&&Objects.equals(String.class, property.getType().getRawClass())) {
            this.strategy = annotation.strategy();
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }
}

创建一个脱敏工具类:

/**
 * 脱敏工具类
 */

public class DesensitizedUtil
{
    /**
     * 密码的全部字符都用*代替,比如:******
     *
     * @param password 密码
     * @return 脱敏后的密码
     */

    public static String password(String password)
    {
        if (StringUtils.isBlank(password))
        {
            return StringUtils.EMPTY;
        }
        return StringUtils.repeat('*', password.length());
    }

    /**
     * 车牌中间用*代替,如果是错误的车牌,不处理
     *
     * @param carLicense 完整的车牌号
     * @return 脱敏后的车牌
     */

    public static String carLicense(String carLicense)
    {
        if (StringUtils.isBlank(carLicense))
        {
            return StringUtils.EMPTY;
        }
        // 普通车牌
        if (carLicense.length() == 7)
        {
            carLicense = StringUtils.hide(carLicense, 3, 6);
        }
        else if (carLicense.length() == 8)
        {
            // 新能源车牌
            carLicense = StringUtils.hide(carLicense, 3, 7);
        }
        return carLicense;
    }
}

创建一个测试类,对其中的数据进行脱敏处理

@Data
public class Person {
    /**
     * 真实姓名
     */

    @Sensitive(strategy = SensitiveStrategy.USERNAME)
    private String realName;
    /**
     * 电话号码
     */

    @Sensitive(strategy = SensitiveStrategy.PHONE)
    private String phoneNumber;
    /**
     * 身份证号码
     */

    @Sensitive(strategy = SensitiveStrategy.ID_CARD)
    private String idCard;
}

创建 测试controller  进行代码测试:

@RestController
public class TestController {
    @GetMapping("/test")
    public Person test(){
        Person user = new Person();
        user.setRealName("阿Q说代码");
        user.setPhoneNumber("13588888888");
        user.setIdCard("370213199204174235");
        return user;
    }
}

使用测试工具进行测试,得出结果:

SpringBoot实战:SpringBoot 数据脱敏




原文始发于微信公众号(Java技术前沿):SpringBoot实战:SpringBoot 数据脱敏

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/299704.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!