范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

聊聊如何自定义数据脱敏

  前言什么是数据脱敏
  数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护 常用脱敏规则
  替换、重排、加密、截断、掩码 良好的数据脱敏实施
  1、尽可能地为脱敏后的应用,保留脱敏前的有意义信息
  2、最大程度地防止黑客进行破解
  今天我们聊聊如何自定义数据脱敏 整体思路
  本示例通过替换的手段实现脱敏,然后配合常用的框架特性,比如mybatis的拦截器机制或者json的序列化来快速实现脱敏 具体落地
  1、定义一个脱敏工具类
  可以直接引用hutool工具包,不过它在5.6+版本以上才提供了这个工具
  https://www.hutool.cn/docs/#/core/工具类/信息脱敏工具-DesensitizedUtil
  不然就自己实现一个,形如下 public class DesensitizedUtils {    	/** 	 * 脱敏,使用默认的脱敏策略 	 * 
 	 * DesensitizedUtil.desensitized("100", DesensitizedUtils.DesensitizedType.USER_ID)) =  "0" 	 * DesensitizedUtil.desensitized("段正淳", DesensitizedUtils.DesensitizedType.CHINESE_NAME)) = "段**" 	 * DesensitizedUtil.desensitized("51343620000320711X", DesensitizedUtils.DesensitizedType.ID_CARD)) = "5***************1X" 	 * DesensitizedUtil.desensitized("09157518479", DesensitizedUtils.DesensitizedType.FIXED_PHONE)) = "0915*****79" 	 * DesensitizedUtil.desensitized("18049531999", DesensitizedUtils.DesensitizedType.MOBILE_PHONE)) = "180****1999" 	 * DesensitizedUtil.desensitized("北京市海淀区马连洼街道289号", DesensitizedUtils.DesensitizedType.ADDRESS)) = "北京市海淀区马********" 	 * DesensitizedUtil.desensitized("duandazhi-jack@gmail.com.cn", DesensitizedUtils.DesensitizedType.EMAIL)) = "d*************@gmail.com.cn" 	 * DesensitizedUtil.desensitized("1234567890", DesensitizedUtils.DesensitizedType.PASSWORD)) = "**********" 	 * DesensitizedUtil.desensitized("苏D40000", DesensitizedUtils.DesensitizedType.CAR_LICENSE)) = "苏D4***0" 	 * DesensitizedUtil.desensitized("11011111222233333256", DesensitizedUtils.DesensitizedType.BANK_CARD)) = "1101 **** **** **** 3256" 	 * 
* * @param str 字符串 * @param desensitizedType 脱敏类型;可以脱敏:用户id、中文名、身份证号、座机号、手机号、地址、电子邮件、密码 * @return 脱敏之后的字符串 * @author dazer and neusoft and qiaomu * @since 5.6.2 */ public static String desensitized(CharSequence str, DesensitizedType desensitizedType) { if (StrUtil.isBlank(str)) { return StrUtil.EMPTY; } String newStr = String.valueOf(str); switch (desensitizedType) { case USER_ID: newStr = String.valueOf(DesensitizedUtils.userId()); break; case CHINESE_NAME: newStr = DesensitizedUtils.chineseName(String.valueOf(str)); break; case ID_CARD: newStr = DesensitizedUtils.idCardNum(String.valueOf(str), 1, 2); break; case FIXED_PHONE: newStr = DesensitizedUtils.fixedPhone(String.valueOf(str)); break; case MOBILE_PHONE: newStr = DesensitizedUtils.mobilePhone(String.valueOf(str)); break; case ADDRESS: newStr = DesensitizedUtils.address(String.valueOf(str), 8); break; case EMAIL: newStr = DesensitizedUtils.email(String.valueOf(str)); break; case PASSWORD: newStr = DesensitizedUtils.password(String.valueOf(str)); break; case CAR_LICENSE: newStr = DesensitizedUtils.carLicense(String.valueOf(str)); break; case BANK_CARD: newStr = DesensitizedUtils.bankCard(String.valueOf(str)); break; default: } return newStr; } /** * 【用户id】不对外提供userId * * @return 脱敏后的主键 */ public static Long userId() { return 0L; } /** * 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李** * * @param fullName 姓名 * @return 脱敏后的姓名 */ public static String chineseName(String fullName) { if (StrUtil.isBlank(fullName)) { return StrUtil.EMPTY; } return StrUtil.hide(fullName, 1, fullName.length()); } /** * 【身份证号】前1位 和后2位 * * @param idCardNum 身份证 * @param front 保留:前面的front位数;从1开始 * @param end 保留:后面的end位数;从1开始 * @return 脱敏后的身份证 */ public static String idCardNum(String idCardNum, int front, int end) { //身份证不能为空 if (StrUtil.isBlank(idCardNum)) { return StrUtil.EMPTY; } //需要截取的长度不能大于身份证号长度 if ((front + end) > idCardNum.length()) { return StrUtil.EMPTY; } //需要截取的不能小于0 if (front < 0 || end < 0) { return StrUtil.EMPTY; } return StrUtil.hide(idCardNum, front, idCardNum.length() - end); } /** * 【固定电话 前四位,后两位 * * @param num 固定电话 * @return 脱敏后的固定电话; */ public static String fixedPhone(String num) { if (StrUtil.isBlank(num)) { return StrUtil.EMPTY; } return StrUtil.hide(num, 4, num.length() - 2); } /** * 【手机号码】前三位,后4位,其他隐藏,比如135****2210 * * @param num 移动电话; * @return 脱敏后的移动电话; */ public static String mobilePhone(String num) { if (StrUtil.isBlank(num)) { return StrUtil.EMPTY; } return StrUtil.hide(num, 3, num.length() - 4); } /** * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区**** * * @param address 家庭住址 * @param sensitiveSize 敏感信息长度 * @return 脱敏后的家庭地址 */ public static String address(String address, int sensitiveSize) { if (StrUtil.isBlank(address)) { return StrUtil.EMPTY; } int length = address.length(); return StrUtil.hide(address, length - sensitiveSize, length); } /** * 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com * * @param email 邮箱 * @return 脱敏后的邮箱 */ public static String email(String email) { if (StrUtil.isBlank(email)) { return StrUtil.EMPTY; } int index = StrUtil.indexOf(email, "@"); if (index <= 1) { return email; } return StrUtil.hide(email, 1, index); } /** * 【密码】密码的全部字符都用*代替,比如:****** * * @param password 密码 * @return 脱敏后的密码 */ public static String password(String password) { if (StrUtil.isBlank(password)) { return StrUtil.EMPTY; } return StrUtil.repeat("*", password.length()); } /** * 【中国车牌】车牌中间用*代替 * eg1:null -》 "" * eg1:"" -》 "" * eg3:苏D40000 -》 苏D4***0 * eg4:陕A12345D -》 陕A1****D * eg5:京A123 -》 京A123 如果是错误的车牌,不处理 * * @param carLicense 完整的车牌号 * @return 脱敏后的车牌 */ public static String carLicense(String carLicense) { if (StrUtil.isBlank(carLicense)) { return StrUtil.EMPTY; } // 普通车牌 if (carLicense.length() == 7) { carLicense = StrUtil.hide(carLicense, 3, 6); } else if (carLicense.length() == 8) { // 新能源车牌 carLicense = StrUtil.hide(carLicense, 3, 7); } return carLicense; } /** * 银行卡号脱敏 * eg: 1101 **** **** **** 3256 * * @param bankCardNo 银行卡号 * @return 脱敏之后的银行卡号 * @since 5.6.3 */ public static String bankCard(String bankCardNo) { if (StrUtil.isBlank(bankCardNo)) { return bankCardNo; } bankCardNo = StrUtil.trim(bankCardNo); if (bankCardNo.length() < 9) { return bankCardNo; } final int length = bankCardNo.length(); final int midLength = length - 8; final StringBuilder buf = new StringBuilder(); buf.append(bankCardNo, 0, 4); for (int i = 0; i < midLength; ++i) { if (i % 4 == 0) { buf.append(CharUtil.SPACE); } buf.append("*"); } buf.append(CharUtil.SPACE).append(bankCardNo, length - 4, length); return buf.toString(); } }   其实正常到这个步骤,通过替换实现脱敏就可以完成,可以直接在程序中,直接调用这个工具就行。但是作为一个懂得偷懒的程序员,肯定不满足这样。于是我们会进一步封装   2、自定义脱敏注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Sensitive { DesensitizedType strategy() default DesensitizedType.NONE; /** * 是否使用dfa算法 * @return */ boolean useDFA() default false; /** * dfa敏感字符替换,默认替换成 "*" * @return */ String dfaReplaceChar() default "*"; /** * dfa敏感字符替换次数 * @return */ int dfaReplaceCharRepeatCount() default 1; }   3、利用一些框架特性提升效率   a、如果项目已经有用mybatis,则可以利用mybatis拦截器特性。实现原理就是拦截响应回来的结果,然后对结果进行脱敏处理 @Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = Statement.class)) public class DesensitizedInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { List list = (List) invocation.proceed(); list.forEach(EntityUtils::desensitized); return list; } }   b、 如果项目是基于springboot的web项目,则可以利用springboot自带的jackson自定义序列化实现。它的实现原来其实就是在json进行序列化渲染给前端时,进行脱敏。   如果是这种方案,则需对自定义注解进行改造一下,加上 @JacksonAnnotationsInside @JsonSerialize(using = DesensitizedJsonSerializer.class)   注解。形如下 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented @JacksonAnnotationsInside @JsonSerialize(using = DesensitizedJsonSerializer.class) public @interface Sensitive { DesensitizedType strategy() default DesensitizedType.NONE; /** * 是否使用dfa算法 * @return */ boolean useDFA() default false; /** * dfa敏感字符替换,默认替换成 "*" * @return */ String dfaReplaceChar() default "*"; /** * dfa敏感字符替换次数 * @return */ int dfaReplaceCharRepeatCount() default 1; }   序列化脱敏逻辑核心代码如下 public class DesensitizedJsonSerializer extends JsonSerializer implements ContextualSerializer { private Sensitive sensitive; @Override public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(EntityUtils.getDesensitizedValue(sensitive,s)); } @Override public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { sensitive = beanProperty.getAnnotation(Sensitive.class); if(!ObjectUtils.isEmpty(sensitive) && String.class.isAssignableFrom(beanProperty.getType().getRawClass())){ return this; } return serializerProvider.findValueSerializer(beanProperty.getType(),beanProperty); } } 示例   以json那种方式为例   1、定义实体对象,需要进行脱敏的属性上加上脱敏注解 @Data @EqualsAndHashCode(callSuper = false) @AllArgsConstructor @NoArgsConstructor @Builder public class UserDTO { private Integer id; private String username; @Sensitive(strategy = DesensitizedType.PASSWORD) private String password; @Sensitive(strategy = DesensitizedType.CHINESE_NAME) private String fullname; @Sensitive(strategy = DesensitizedType.MOBILE_PHONE) private String mobile; @Sensitive(strategy = DesensitizedType.EMAIL) private String email; @Sensitive(useDFA = true,dfaReplaceChar = "#",dfaReplaceCharRepeatCount = 3) private String remark; }   2、编写一个测试controller @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping(value="/list") public AjaxResult listUsers(){ return AjaxResult.success(userService.listUserDTO()); } }   测试结果   如图所示已经进行脱敏 其他方案   1、基于Sharding Sphere实现数据脱敏   具体实现可以参考如下文章   https://jaskey.github.io/blog/2020/03/18/sharding-sphere-data-desensitization/   2、自定义注解格式化   主要实现步骤如下 1、实现AnnotationFormatterFactory接口 2、创建脱敏格式化类实现Formatter 3、将AnnotationFormatterFactory实现的接口注册到FormatterRegistry   具体实现可以参考如下文章   https://blog.csdn.net/qq_27081015/article/details/103295983   4、利用fastjson进行脱敏   主要实现步骤如下 1、实现ValueFilter接口,在process进行脱敏 2、配置fastjson为默认JSON转换 /** * 配置fastjson为默认JSON转换 * * @return */ @Bean public HttpMessageConverters fastJsonHttpMessageConverters() { // 1.定义一个converters转换消息的对象 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); // 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据 FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter());//添加自己写的拦截器 // 3.在converter中添加配置信息 fastConverter.setFastJsonConfig(fastJsonConfig); // 4.将converter赋值给HttpMessageConverter HttpMessageConverter<?> converter = fastConverter; // 5.返回HttpMessageConverters对象 return new HttpMessageConverters(converter); }   具体实现可以参考如下文章   https://blog.csdn.net/qq_27081015/article/details/103297316   5、利用mybatis-mate   mybatis-plus 企业(数据优雅处理)模块,使用时要配置一下授权码。如下 mybatis-mate: cert: grant: jinTianYiXueKe license: GKXP9r4MCJhGID/DTGigcBcLmZjb1YZGjE4GXaAoxbtGsPC20sxpEtiUr2F7Nb1ANTUekvF6Syo6DzraA4M4oacwoLVTglzfvaEyUogW8L7mydqlsZ4+hlm20kK85eLJK1QsskrSJmreMnEaNh9lsV7Lpbxy9JeGCeM0HPEbRvq8Y+8dUt5bQYLklsa3ZIBexir+4XykZY15uqn1pYIp4pEK0+aINTa57xjJNoWuBIqm7BdFIb4l1TAcPYMTsMXhF5hfMmKD2h391HxWTshJ6jbt4YqdKD167AgeoM+B+DE1jxlLjcpskY+kFs9piOS7RCcmKBBUOgX2BD/JxhR2gQ==   他的实现机理就是利用json序列化那种,如果感兴趣可以参考如下链接   https://gitee.com/baomidou/mybatis-mate-examples   本文的demo也有基于mybatis-mate实现脱敏,链接如下   https://github.com/lyb-geek/springboot-learning/tree/master/springboot-desensitization/springboot-desensitzation-mybatis-mate 总结   有时候业务场景的实现方式有多种多样,大家要懂得取舍判断,比如上面的方案如果你的项目本来就没用mybatis,但为了脱敏又引入mybatis,这种方案就额外有加入了复杂度,后面维护估计就有得折腾了 demo链接   https://github.com/lyb-geek/springboot-learning/tree/master/springboot-desensitization
华为又一套牌机?HiNova9SE即将上市华为在手机业务这块,目前也是将主要精力放在了主打的旗舰系列,而旗下的麦芒畅享以及Nova系列的机型算是出售给国内运营商以及其他公司机构了,也是为了让华为手机业务能够换个方式存活下去华为赋能AITO问界M7实车图曝光日前,我们从相关渠道获得了一组AITO问界M7的实车图片,AITO是华为与赛里斯联手打造的品牌,主打科技智能化。问界M7的尺寸将超过问界M5,定位为中大型SUV,长宽高分别为502科学家归纳想长寿每天所需的最佳步数不是1万步古老的智慧让我们相信说,每天走一万步能够带领我们过一个长而健康的人生。对那些生活中常常坐着的人而言,这是一个需要一些努力才能维持的目标。而现在,我们也已经知道有一段时间,这个步数概京东物流自杀式援沪后股价反而大涨受益物流国七条物流股普涨4月10日,中共中央国务院发布关于加快建设全国统一大市场的意见,提出建设现代流通网络。11日,国务院联防联控机制发布关于切实做好货运物流保通保畅工作的通知,推出推进物流保通保畅的国因数据造假贾跃亭被解除FF执行官职务深陷数据造假危机的新造车公司FF(法拉第未来)发布公告称,公司创始人前CEO贾跃亭被解除了执行官职务。贾跃亭的造车还债之路再遇波折。法拉第未来公告称,作为已经结束的内部调查的一部分python微信也不过如此嘛,这不公众号信息被我采集下来啦前言嗨喽!大家好呀,这里是魔王知识点基本流程fiddler抓包开发环境python3。8运行代码pycharm2021。2辅助敲代码requests第三方模块如果安装python第Windows内存映射文件前言在查看mono源码的时候,发现在加载mscorlib。dll的时候,使用的是内存映射文件。不管在Windows系统还是Linux系统,都有对应内存映射文件API。内存映射文件可埃夫特ER10系列机器人在五金行业搬运应用埃夫特ER10系列机器人在五金行业搬运应用ER10C60是一款6轴工业机器人,它的最大特色是本体手腕部分采用特殊结构,能够在收到外来力矩作用后,分解减速机受到的力矩,增加减速机的使杰华特冲刺科创板上市新动态,IPO进入已问询状态4月18日,资本邦了解到,杰华特微电子股份有限公司(下称杰华特)科创板IPO进入已问询状态。图片来源上交所官网杰华特是以虚拟IDM为主要经营模式的模拟集成电路设计企业,专业从事模拟新能源车保值率怎么样?新能源车保值率怎么样?说到保值率我们绝大部分人会想到日系车,想到飞度,开两年还能卖个新车的价格,可聊到新能源的话大多数人会觉得如今的新能源就是隔韭菜,那里来的保值率可讲?其实原因有俄媒乌克兰公司企图对俄银行用户发起大规模攻击被成功阻止海外网4月18日电俄新社18日报道称,俄罗斯联邦储蓄银行表示,在特别军事行动开始后,乌克兰应用程序开发商企图对俄罗斯银行用户发起大规模攻击,但俄罗斯联邦储蓄银行阻止了这一行动。俄联
特斯拉2022年05月02日新消息特斯拉特斯拉2022年05月02日新消息比亚迪不如特斯拉?差距有多大?谁才是新能源汽车的王者别管马斯克的推特和可口可乐了这个消息更重要紧急召回通知!国家市场监管总局公布新一批汽车召自研芯加持,影像玩法多,vivoX80系列直达影像天花板近年来,vivo在计算摄影和光学技术这两个手机影像的关键领域都取得了重大突破,为大家带来了影像性能更加强大的旗舰手机。全新登场的vivoX80系列在影像方面再次进行了升级,不仅搭载想换手机了苹果SEXR苹果11你会怎么选?手机已经开始卡了,想换了,由于之前一直用苹果6S,习惯了,现在上面三款机都不贵,而且实用性高,办公为主我觉得很合适,但是不知道怎么选才好了。现在很多人选择在淘宝买手机,一些品牌店销手机耗电太快怎么解决?每次出门最担心的莫过于,在吃吃喝喝买买买的时候,发现手机电量告警,瞬间紧张!手机为什么耗电这么快呢?排除电池损坏,还与你的使用习惯有关,快看你中了几条?视频加载中1手机屏幕亮度过高三星再次超越苹果称霸全球智能手机市场在多数情况下,三星一直都是位居全球智能手机市场的头把交椅。在被苹果反击之后,现在三星又重新夺回了销量冠军的名衔。来自市场调研公司Canalys和Counterpoint的数据显示,微信正在输入咋回事?对方并非在回你消息,知道原因后挺伤人微信逐渐的渗透到人们的日常生活当中,微信的用户数量也在不断的增加,而当用户与对方聊天的时候,就会发现聊天页面显示正在输入,然而实际上,对方或许并非是正在回复消息,也很有可能是对方看马云或许没有吹牛,电商或将被淘汰,全新商业模式已经悄然而生电子商务的发展,很大程度上方便了人们的生活,而当初并不被看好的马云,如今也随着阿里巴巴的成功,身价有了很大的提高,然而,就在电子商务不断向前发展的时候,马云却认为接下来电商将会被淘阿尔卑斯山发现巨型鱼龙化石据路透社4月28日报道,科学家在瑞士阿尔卑斯山高处发现巨型海洋爬行动物化石。报道说,研究人员在瑞士阿尔卑斯山三座山峰之上海拔2740米的非常规地点发现了地球海洋中有史以来的最大型生Windows系统bat批处理常用命令(一)一批处理中常用的命令命令加在每个命令行的最前面,表示运行时不显示这一行的命令行。egechooff不显示后续命令行及当前命令行attrib设置文件属性ATTRIBRRAASSHHd最好的帮手往往是在你需要的时候出现,vivoX80便是如此随着五一假期的到来,想要在劳动节期间买新机犒劳一下认真劳动自己的人,那么vivo前段时间刚发布的vivoX系列的影像旗舰vivoX80将会是你的一个不二选择。何出此言?那请听我细细发布5个月下跌至2399元,256GB66W独显,骁龙888为清仓彻底拼了iQOO近些年在国内手机市场可以说是风生水起,以游戏体验为核心卖点,打造了不少独具特色的手机,比如iQOONeo5iQOO7以及本文要说的iQOONeo5S,从命名可以看出iQOO