当前位置: 首页 > 科技观察

如何对数据进行脱敏?

时间:2023-03-19 20:28:19 科技观察

1。背景在实际业务开发过程中,我们经常需要对用户的隐私数据进行脱敏处理。所谓脱敏过程,其实就是对数据进行混淆和隐藏。例如下图中,用户的手机号码、地址等数据信息用*号隐藏,避免个人隐私信息泄露。如果需要脱敏的数据范围很小,哪怕是一个指定的字段,一般的处理方法也很简单,写一个隐藏方法就可以实现数据脱敏。如果需求不多,实现这个方法问题不大,而且易于维护!但是如果是像上面这样位置很多的数据,就需要按类别脱敏。通过这种简单粗暴的处理,代码似乎少了些优雅。想一想,我们是不是可以在数据输出的阶段进行统一的数据脱敏处理,这样可以省去很多体力劳动。说到数据输出,很多同学可能会想到JSON序列化。没错,我们熟悉的web系统就是通过json将数据序列化,展示给前端。那么问题来了,序列化时如何对数据进行脱敏呢?废话不多说,直接写代码!2.程序实践2.1.首先添加依赖包。spring-web包或者spring-boot-starter-web包,因为这些jar包都集成了jackson相关的包,所以不需要重复依赖。如果当前项目没有jackson包,可以通过以下方式添加相关依赖包。com.fasterxml.jackson.corejackson-core2.9.8com.fasterxml.jackson.corejackson-annotations<版本>2.9.8com.fasterxml.jackson.corejackson-databind2.9.82.2,编写脱敏类型枚举类,满足不同场景的处理publicenumSensitiveEnum{/***中文姓名*/CHINESE_NAME,/***身份证号码*/ID_CARD,/***座机号码*/FIXED_PHONE,/***手机号码*/MOBILE_PHONE,/***地址*/ADDRESS,/***电子邮箱*/EMAIL,/***银行卡*/BANK_CARD,/***公司银行账户*/CNAPS_CODE}2.3.编写脱敏注解类.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)@JacksonAnnotationsInside@JsonSerialize(using=SensitiveSerialize.class)public@interfaceSensitiveWrapped{/***脱敏类*@return*/SensitiveEnumvalue();}2.4、编写脱顺序列表化类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;importjava.io.IOException;importjava.util.Objects;publicclassSensitiveSerializeextendsJsonSerializerimplementsContextualSerializer{/***脱敏类*/privateSensitiveEnumtype;@OverridepublicStrings,serialize(JsonGeneratorjsonGenerator,SerializerProviderserializerProvider)throwsIOException{switch(this.type){caseCHINESE_NAME:{jsonGenerator.writeString(SensitiveInfoUtils.chineseName(s));break;}caseID_CARD:{jsonGenerator.writeString(SensitiveInfoUtils.idCardNum(s));break;}caseFIXED_PHONE:{jsonGenerator.writeString(SensitiveInfoUtils.fixedPhone(s));break;}caseMOBILE_PHONE:{jsonGenerator.writeString(SensitiveInfoUtils.mobilePhone(s));break;}caseADDRESS:{jsonGenerator.writeString(SensitiveInfoUtils.address(s,4));break;}caseEMAIL:{jsonGenerator.writeString(SensitiveInfoUtils.email(s));break;}caseBANK_CARD:{jsonGenerator.writeString(SensitiveInfoUtils.bankCard(s));break;}caseCNAPS_CODE:{jsonGenerator.writeString(SensitiveInfoUtils.cnapsCode(s));break;}}}@OverridepublicJsonSerializercreateContextual(SerializerProviderserializerProvider,BeanPropertybeanProperty)throwsJsonMappingException{//为空直跳过if(beanProperty!=null){//非字符串类直接跳过if(Objects.equals(beanProperty.getType().getRawClass(),String.class)){SensitiveWrappedsensitiveWrapped=beanProperty.getAnnotation(SensitiveWrapped.class);if(sensitiveWrapped==null){sensitiveWrapped=beanProperty.getContextAnnotation(SensitiveWrapped.class);}if(sensitiveWrapped!=null){//如果可以获取注解,将注解的值传入SensitiveSerializereturnnewSensitiveSerialize(sensitiveWrapped.value());}}returnserializerProvider.findValueSerializer(beanProperty.getType(),beanProperty);}returnserializerProvider.findNullValueSerializer(beanProperty);}publicSensitiveSerialize(){}publicSensitiveSerialize(finalSensitiveEnumtype){this.type=type;}}createContextual的作用是通过JsonSerializer已知的上下文信息自定义JsonSerializer对象场地2.5.编写脱敏工具类importorg.apache.commons.lang3.StringUtils;publicclassSensitiveInfoUtils{/***[中文名]只显示第一个汉字,其他隐藏为2个星号<例:李**>*/publicstaticStringchineseName(finalStringfullName){if(StringUtils.isBlank(fullName)){return"";}finalStringname=StringUtils.left(fullName,1);returnStringUtils.rightPad(name,StringUtils.length(fullName),"*");}/***[中文名称]只显示第一个汉字,并且其他的被隐藏为2个星号*/publicstaticStringchineseName(finalStringfamilyName,finalStringgivenName){if(StringUtils.isBlank(familyName)||StringUtils.isBlank(givenName)){return"";}returnchineseName(familyName+givenName);}/***[IDnumber]显示最后四位数字,并隐藏其他数字。总共18或15位数字。<示例:420**********5762>*/publicstaticStringidCardNum(finalStringid){if(StringUtils.isBlank(id)){return"";}returnStringUtils.left(id,3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(id,4),StringUtils.length(id),"*"),"***"));}/***[固话]后四位,其他隐藏<示例:****1234>*/publicstaticStringfixedPhone(finalStringnum){if(StringUtils.isBlank(num)){return"";}returnStringUtils.leftPad(StringUtils.right(num,4),StringUtils.length(num),"*");}/***[手机号码]前三位,后四位,其他隐藏<例:138******1234>*/publicstaticStringmobilePhone(finalStringnum){if(StringUtils.isBlank(num)){return"";}returnStringUtils.left(num,3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num,4),StringUtils.length(num),"*"),"***"));}/***[地址]只显示区域,不显示详细地址;我们要加强对个人信息的保护<例:北京市海淀区****>**@paramsensitiveSize敏感信息长度*/publicstaticStringaddress(finalStringaddress,finalintsensitiveSize){if(StringUtils.isBlank(address)){return"";}finalintlength=StringUtils.length(address);returnStringUtils.rightPad(StringUtils.left(address,length-sensitiveSize),length,"*");}/***[E-mail]只显示邮箱前缀首字母,隐藏前缀,用星号代替。@和以下地址显示*/publicstaticStringemail(finalStringemail){if(StringUtils.isBlank(email)){return"";}finalintindex=StringUtils.indexOf(email,"@");if(index<=1){returnemail;}else{returnStringUtils.rightPad(StringUtils.left(email,1),index,"*").concat(StringUtils.mid(email,index,StringUtils.length(email))));}}/***[银行卡号]前六位,后四位,其他用Asterisk的每比特隐藏1个星号<例子:6222600************1234>*/publicstaticStringbankCard(finalStringcardNum){if(StringUtils.isBlank(cardNum)){return"";}returnStringUtils.left(cardNum,6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum,4),StringUtils.length(cardNum),"*"),"******"));}/***[公司银行账号]公司账户银行账号,显示在前面两个数字,其他数字用星号隐藏,每个数字都有一个星号returnStringUtils.rightPad(StringUtils.left(code,2),StringUtils.length(code),"*");}}2.6。写一个测试实体类最后我们写一个实体类UserEntity看看转换后效果如何?publicclassUserEntity{/***用户ID*/privateLonguserId;/***用户名*/privateStringname;/***手机号码*/@SensitiveWrapped(SensitiveEnum.MOBILE_PHONE)privateStringmobile;/***身份证号码*/@SensitiveWrapped(SensitiveEnum.ID_CARD)privateStringidCard;/***age*/privateStringsex;/***sex*/privateintage;//省略get,set...}测试过程如下:publicclassSensitiveDemo{publicstaticvoidmain(String[]args)throwsJsonProcessingException{UserEntityuserEntity=newUserEntity();userEntity.setUserId(1l);userEntity.setName("张三");userEntity.setMobile("18000000001");userEntity.setIdCard("42011720000101100008888");userEntity.setAge(2);用户实体。setSex("male");//通过jackson将对象序列化为json字符串ObjectMapperobjectMapper=newObjectMapper();System.out.println(objectMapper.writeValueAsString(userEntity));}}结果如下:{"userId":1,"name":"张三","mobile":"180****0001","idCard":"420******************8888","sex":"Male","age":20}是看的很清楚,转换结果成功!如果你现在的项目是基于SpringMVC框架开发的,返回对象时,框架会自动为你使用jackson框架序列化@RequestMapping("/hello")publicUserEntityhello(){UserEntityuserEntity=newUserEntity();userEntity.setUserId(1l);userEntity.setName("张三");userEntity.setMobile("18000000001");userEntity.setIdCard("420117200001011000008888");userEntity.setAge(20);userEntity.setSex("男");returnuserEntity;}请求网页http://127.0.0.1:8080/hello,结果如下:3.总结在实际业务场景开发中,使用注解对全局数据进行脱敏,可以有效解决敏感数据隐私泄露问题。本文主要从实用层面对数据脱敏处理做一个简单的介绍。一些网友可能有更好的解决办法。欢迎在下方留言。以后如果遇到好的解决方法,会分享给大家。有帮助!4.参考1.CSDN-注解实现json序列化时自动数据脱敏2.yanbin.blog-自定义Jackson注解并禁用特定注解3.简书-数据脱敏处理