0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何实现SpringBoot项目中的隐私数据脱敏呢

jf_ro2CN3Fa 来源:csdn 2023-01-08 10:25 次阅读

这两天在整改等保测出的问题,里面有一个“用户信息泄露”的风险项(就是后台系统里用户的一些隐私数据直接明文显示了),其实指的就是要做数据脱敏。

数据脱敏:把系统里的一些敏感数据进行加密处理后再返回,达到保护隐私作用,实现效果图如下:

603865d0-8ef9-11ed-bfe3-dac502259ad0.png

其实要实现上面的效果,可能最先想到的方法是直接改每个controller接口,在返回数据前做一次加密处理,当然这个方法肯定是非常捞的。这里推荐用注解来实现,即高效又优雅,省时省力,支持扩展。

其实解决方案大体上分两种:

在拿到数据时就已经脱敏了(如在 mysql 查询时用 insert 函数进行隐藏)

拿到数据后在序列化的时候再进行脱敏(如用 fastjson、jackson)

这里我所选用的方案是第二种,即在接口返回数据前,在序列化的时候对敏感字段值进行处理,并且选用 jackson 的序列化来实现(推荐)

1. 创建隐私数据类型枚举:PrivacyTypeEnum

importlombok.Getter;

/**
*隐私数据类型枚举
*/
@Getter
publicenumPrivacyTypeEnum{

/**自定义(此项需设置脱敏的范围)*/
CUSTOMER,

/**姓名*/
NAME,

/**身份证号*/
ID_CARD,

/**手机号*/
PHONE,

/**邮箱*/
EMAIL,
}

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

2. 创建自定义隐私注解:PrivacyEncrypt

importcom.fasterxml.jackson.annotation.JacksonAnnotationsInside;
importcom.fasterxml.jackson.databind.annotation.JsonSerialize;

importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;

/**
*自定义数据脱敏注解
*/
@Target(ElementType.FIELD)//作用在字段上
@Retention(RetentionPolicy.RUNTIME)//class文件中保留,运行时也保留,能通过反射读取到
@JacksonAnnotationsInside//表示自定义自己的注解PrivacyEncrypt
@JsonSerialize(using=PrivacySerializer.class)//该注解使用序列化的方式
public@interfacePrivacyEncrypt{

/**
*脱敏数据类型(没给默认值,所以使用时必须指定type)
*/
PrivacyTypeEnumtype();

/**
*前置不需要打码的长度
*/
intprefixNoMaskLen()default1;

/**
*后置不需要打码的长度
*/
intsuffixNoMaskLen()default1;

/**
*用什么打码
*/
Stringsymbol()default"*";
}

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

3. 创建自定义序列化器:PrivacySerializer

importcom.fasterxml.jackson.core.JsonGenerator;
importcom.fasterxml.jackson.databind.BeanProperty;
importcom.fasterxml.jackson.databind.JsonMappingException;
importcom.fasterxml.jackson.databind.JsonSerializer;
importcom.fasterxml.jackson.databind.SerializerProvider;
importcom.fasterxml.jackson.databind.ser.ContextualSerializer;
importcom.zk.common.core.domain.enumerate.PrivacyTypeEnum;
importcom.zk.common.core.utils.PrivacyUtil;
importjava.io.IOException;
importjava.util.Objects;
importlombok.AllArgsConstructor;
importlombok.NoArgsConstructor;
importorg.apache.commons.lang3.StringUtils;

@NoArgsConstructor
@AllArgsConstructor
publicclassPrivacySerializerextendsJsonSerializerimplementsContextualSerializer{

//脱敏类型
privatePrivacyTypeEnumprivacyTypeEnum;
//前几位不脱敏
privateIntegerprefixNoMaskLen;
//最后几位不脱敏
privateIntegersuffixNoMaskLen;
//用什么打码
privateStringsymbol;

@Override
publicvoidserialize(finalStringorigin,finalJsonGeneratorjsonGenerator,
finalSerializerProviderserializerProvider)throwsIOException{
if(StringUtils.isNotBlank(origin)&&null!=privacyTypeEnum){
switch(privacyTypeEnum){
caseCUSTOMER:
jsonGenerator.writeString(PrivacyUtil.desValue(origin,prefixNoMaskLen,suffixNoMaskLen,symbol));
break;
caseNAME:
jsonGenerator.writeString(PrivacyUtil.hideChineseName(origin));
break;
caseID_CARD:
jsonGenerator.writeString(PrivacyUtil.hideIDCard(origin));
break;
casePHONE:
jsonGenerator.writeString(PrivacyUtil.hidePhone(origin));
break;
caseEMAIL:
jsonGenerator.writeString(PrivacyUtil.hideEmail(origin));
break;
default:
thrownewIllegalArgumentException("unknownprivacytypeenum"+privacyTypeEnum);
}
}
}

@Override
publicJsonSerializercreateContextual(finalSerializerProviderserializerProvider,
finalBeanPropertybeanProperty)throwsJsonMappingException{
if(beanProperty!=null){
if(Objects.equals(beanProperty.getType().getRawClass(),String.class)){
PrivacyEncryptprivacyEncrypt=beanProperty.getAnnotation(PrivacyEncrypt.class);
if(privacyEncrypt==null){
privacyEncrypt=beanProperty.getContextAnnotation(PrivacyEncrypt.class);
}
if(privacyEncrypt!=null){
returnnewPrivacySerializer(privacyEncrypt.type(),privacyEncrypt.prefixNoMaskLen(),
privacyEncrypt.suffixNoMaskLen(),privacyEncrypt.symbol());
}
}
returnserializerProvider.findValueSerializer(beanProperty.getType(),beanProperty);
}
returnserializerProvider.findNullValueSerializer(null);
}
}

这里是具体的实现过程,因为要脱敏的数据都是 String 类型的,所以继承 JsonSerializer 时的类型填 String

重写的 serialize 方法是实现脱敏的核心,根据类型 type 的不同去设置序列化后的值

重写的 createContextual 方法就是去读取我们自定义的 PrivacyEncrypt 注解,打造一个上下文的环境。

4. 隐私数据隐藏工具类:PrivacyUtil

publicclassPrivacyUtil{

/**
*隐藏手机号中间四位
*/
publicstaticStringhidePhone(Stringphone){
returnphone.replaceAll("(\d{3})\d{4}(\d{4})","$1****$2");
}

/**
*隐藏邮箱
*/
publicstaticStringhideEmail(Stringemail){
returnemail.replaceAll("(\w?)(\w+)(\w)(@\w+\.[a-z]+(\.[a-z]+)?)","$1****$3$4");
}

/**
*隐藏身份证
*/
publicstaticStringhideIDCard(StringidCard){
returnidCard.replaceAll("(\d{4})\d{10}(\w{4})","$1*****$2");
}

/**
*【中文姓名】只显示第一个汉字,其他隐藏为星号,比如:任**
*/
publicstaticStringhideChineseName(StringchineseName){
if(chineseName==null){
returnnull;
}
returndesValue(chineseName,1,0,"*");
}

///**
//*【身份证号】显示前4位,后2位
//*/
//publicstaticStringhideIdCard(StringidCard){
//returndesValue(idCard,4,2,"*");
//}

///**
//*【手机号码】前三位,后四位,其他隐藏。
//*/
//publicstaticStringhidePhone(Stringphone){
//returndesValue(phone,3,4,"*");
//}

/**
*对字符串进行脱敏操作
*@paramorigin原始字符串
*@paramprefixNoMaskLen左侧需要保留几位明文字段
*@paramsuffixNoMaskLen右侧需要保留几位明文字段
*@parammaskStr用于遮罩的字符串,如'*'
*@return脱敏后结果
*/
publicstaticStringdesValue(Stringorigin,intprefixNoMaskLen,intsuffixNoMaskLen,StringmaskStr){
if(origin==null){
returnnull;
}
StringBuildersb=newStringBuilder();
for(inti=0,n=origin.length();i< n; i++) {
            if (i < prefixNoMaskLen) {
                sb.append(origin.charAt(i));
                continue;
            }
            if (i >(n-suffixNoMaskLen-1)){
sb.append(origin.charAt(i));
continue;
}
sb.append(maskStr);
}
returnsb.toString();
}

publicstaticvoidmain(String[]args){
System.out.println(hideChineseName("张三三"));
}
}

这个工具类其实可以自己定,根据自己的业务去扩展,提一点:

在自定义注解 PrivacyEncrypt 里,只有 type 的值为 PrivacyTypeEnum.CUSTOMER(自定义)时,才需要指定脱敏范围,即 prefixNoMaskLen 和 suffixNoMaskLen 的值,像邮箱、手机号这种隐藏格式都采用固定的

5. 注解使用

直接在需要脱敏的字段上加上注解,指定 type 值即可,如下:

@Data
publicclassPeople{

privateIntegerid;

privateStringname;

privateIntegersex;

privateIntegerage;

@PrivacyEncrypt(type=PrivacyTypeEnum.PHONE)//隐藏手机号
privateStringphone;

@PrivacyEncrypt(type=PrivacyTypeEnum.EMAIL)//隐藏邮箱
privateStringemail;

privateStringsign;
}

到这里,脱敏工作就已经结束了,全局使用这一个注解即可,一劳永逸,测试效果图如下:

604f1096-8ef9-11ed-bfe3-dac502259ad0.png

以上代码已经过测试,并已实际使用,所以可放心使用。







审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MySQL
    +关注

    关注

    1

    文章

    775

    浏览量

    26005
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106

原文标题:仅需一个注解,实现 SpringBoot 项目中的隐私数据脱敏!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    SpringBoot 如何实现热部署

    SpringBoot 如何实现热部署? 1、热部署的优点 开发周期通常包括编写代码、编译、部署和测试几个步骤。在一个快速发展的项目中,这个周期需要尽可能地缩短。热部署能让开发者在代码更改后立即看到结果,从而加速开发和测试过程。
    的头像 发表于 09-30 10:16 414次阅读
    <b class='flag-5'>SpringBoot</b> 如何<b class='flag-5'>实现</b>热部署

    SpringBoot项目数据源配置数据

    SpringBoot项目数据源配置
    发表于 06-05 09:51

    怎样去使用springboot

    怎样去使用springboot?学习springboot需要懂得哪些?
    发表于 10-25 07:13

    推荐两个工作流的springboot项目

    今天主要推荐两个工作流的springboot项目,开源项目中有具体的部署操作文档,核心表结构说明,都可以帮助理解工作流原理,其实大厂华为阿里里面的工作流虽然号称自研(很多都是参考开源),跟开源工作流的原理差不多的。
    的头像 发表于 03-18 09:31 1413次阅读

    SQL/JAVA/mybatisplus数据脱敏实现原理

    MYSQL(电话号码,身份证)数据脱敏实现
    的头像 发表于 06-14 17:09 3129次阅读

    如何在SpringBoot项目中实现动态定时任务

    之前写过文章记录怎么在SpringBoot项目中简单使用定时任务,不过由于要借助cron表达式且都提前定义好放在配置文件里,不能在项目运行中动态修改任务执行时间,实在不太灵活。
    的头像 发表于 09-30 11:16 1492次阅读

    数据脱敏技术定义及实施过程解析

    数据脱敏数据安全领域落地场景较为成熟的技术手段,在数据深层次、大范围的共享开放的今天,数据脱敏
    的头像 发表于 11-16 10:26 2111次阅读

    SpringBoot日志脱敏的方式介绍

    一个项目在书写了很多打印日志的代码,但是后面有了脱敏需求,如果我们去手动改动代码,会花费大量时间。如果引入本组件,完成配置即可轻松完成脱敏。(仅需三步可轻松配置)
    的头像 发表于 01-09 10:49 2010次阅读

    如何正确使用SpringBoot项目中缓存Cache

    缓存可以通过将经常访问的数据存储在内存中,减少底层数据源如数据库的压力,从而有效提高系统的性能和稳定性。我想大家的项目中或多或少都有使用过,我们项目
    的头像 发表于 05-11 11:01 1042次阅读
    如何正确使用<b class='flag-5'>SpringBoot</b><b class='flag-5'>项目中</b>缓存Cache

    SpringBoot项目中使用缓存的正确方法

      前言 缓存可以通过将经常访问的数据存储在内存中,减少底层数据源如数据库的压力,从而有效提高系统的性能和稳定性。我想大家的项目中或多或少都有使用过,我们
    的头像 发表于 06-13 10:59 2641次阅读
    <b class='flag-5'>SpringBoot</b><b class='flag-5'>项目中</b>使用缓存的正确方法

    如何实现一个注解进行数据脱敏

    类 4、测试 后记   本文主要分享什么是数据脱敏,如何优雅的在项目中运用一个注解实现数据脱敏
    的头像 发表于 06-14 09:37 671次阅读
    如何<b class='flag-5'>实现</b>一个注解进行<b class='flag-5'>数据</b><b class='flag-5'>脱敏</b>

    Springboot项目的集成以及具体使用及配置

      概念 核心组件 API介绍 Springboot集成 具体业务集成 API使用   前言 项目中需要用到工作流引擎来设计部分业务流程,框架选型最终选择了 Camunda7,关于 Camunda
    的头像 发表于 07-03 11:18 899次阅读
    <b class='flag-5'>Springboot</b><b class='flag-5'>项目</b>的集成以及具体使用及配置

    什么是数据脱敏?常用的脱敏规则有哪些呢?

    数据脱敏,指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私
    的头像 发表于 08-15 10:04 9993次阅读
    什么是<b class='flag-5'>数据</b><b class='flag-5'>脱敏</b>?常用的<b class='flag-5'>脱敏</b>规则有哪些呢?

    SpringBoot采用JsonSerializer和Aop实现可控制的数据脱敏

    的确实现数据脱敏,但是有个问题现在的脱敏针对的是 只要对该实体类进行了使用返回的接口,中的数据都会进行
    的头像 发表于 11-06 16:15 430次阅读
    <b class='flag-5'>SpringBoot</b>采用JsonSerializer和Aop<b class='flag-5'>实现</b>可控制的<b class='flag-5'>数据</b><b class='flag-5'>脱敏</b>

    数据脱敏的3种常见方案,好用到爆!

    数据脱敏插件,目前支持地址脱敏、银行卡号脱敏、中文姓名脱敏、固话脱敏、身份证号
    的头像 发表于 02-25 16:14 361次阅读
    <b class='flag-5'>数据</b><b class='flag-5'>脱敏</b>的3种常见方案,好用到爆!