文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

如何对数据进行脱敏处理?

2024-12-02 18:42

关注

如果需要脱敏的数据范围很小很小,甚至就是指定的字段,一般的处理方式也很简单,就是写一个隐藏方法即可实现数据脱敏。

如果是需求很少的情况下,采用这种方式实现没太大问题,好维护!

但如果是类似上面那种很多位置的数据,需要分门别类的进行脱敏处理,通过这种简单粗暴的处理,代码似乎就显得不太优雅了。

思考一下,我们可不可以在数据输出的阶段,进行统一数据脱敏处理,这样就可以省下不少体力活。

说到数据输出,很多同学可能会想到 JSON 序列化。是的没错,我们所熟悉的 web 系统,就是将数据通过 json 序列化之后展示给前端。

那么问题来了,如何在序列化的时候,进行数据脱敏处理呢?

废话不多说,代码直接撸上!

二、程序实践

2.1、首先添加依赖包

默认的情况下,如果当前项目已经添加了spring-web包或者spring-boot-starter-web包,因为这些jar包已经集成了jackson相关包,因此无需重复依赖。

如果当前项目没有jackson包,可以通过如下方式进行添加相关依赖包。

  1. --jackson依赖--> 
  2.  
  3.     com.fasterxml.jackson.core 
  4.     jackson-core 
  5.     2.9.8 
  6.  
  7.  
  8.     com.fasterxml.jackson.core 
  9.     jackson-annotations 
  10.     2.9.8 
  11.  
  12.  
  13.     com.fasterxml.jackson.core 
  14.     jackson-databind 
  15.     2.9.8 
  16.  

 

2.2、编写脱敏类型枚举类,满足不同场景的处理

  1. public enum SensitiveEnum { 
  2.  
  3.      
  4.     CHINESE_NAME, 
  5.  
  6.      
  7.     ID_CARD, 
  8.  
  9.      
  10.     FIXED_PHONE, 
  11.  
  12.      
  13.     MOBILE_PHONE, 
  14.  
  15.      
  16.     ADDRESS, 
  17.  
  18.      
  19.     EMAIL, 
  20.  
  21.      
  22.     BANK_CARD, 
  23.  
  24.      
  25.     CNAPS_CODE 

2.3、编写脱敏注解类

  1. import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; 
  2. import com.fasterxml.jackson.databind.annotation.JsonSerialize; 
  3. import java.lang.annotation.Retention; 
  4. import java.lang.annotation.RetentionPolicy; 
  5.  
  6. @Retention(RetentionPolicy.RUNTIME) 
  7. @JacksonAnnotationsInside 
  8. @JsonSerialize(using = SensitiveSerialize.class) 
  9. public @interface SensitiveWrapped { 
  10.  
  11.      
  12.     SensitiveEnum value(); 

2.4、编写脱敏序列化类

  1. import com.fasterxml.jackson.core.JsonGenerator; 
  2. import com.fasterxml.jackson.databind.BeanProperty; 
  3. import com.fasterxml.jackson.databind.JsonMappingException; 
  4. import com.fasterxml.jackson.databind.JsonSerializer; 
  5. import com.fasterxml.jackson.databind.SerializerProvider; 
  6. import com.fasterxml.jackson.databind.ser.ContextualSerializer; 
  7. import java.io.IOException; 
  8. import java.util.Objects; 
  9.  
  10. public class SensitiveSerialize extends JsonSerializer implements ContextualSerializer { 
  11.  
  12.      
  13.     private SensitiveEnum type; 
  14.  
  15.      
  16.     @Override 
  17.     public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 
  18.         switch (this.type) { 
  19.             case CHINESE_NAME: { 
  20.                 jsonGenerator.writeString(SensitiveInfoUtils.chineseName(s)); 
  21.                 break; 
  22.             } 
  23.             case ID_CARD: { 
  24.                 jsonGenerator.writeString(SensitiveInfoUtils.idCardNum(s)); 
  25.                 break; 
  26.             } 
  27.             case FIXED_PHONE: { 
  28.                 jsonGenerator.writeString(SensitiveInfoUtils.fixedPhone(s)); 
  29.                 break; 
  30.             } 
  31.             case MOBILE_PHONE: { 
  32.                 jsonGenerator.writeString(SensitiveInfoUtils.mobilePhone(s)); 
  33.                 break; 
  34.             } 
  35.             case ADDRESS: { 
  36.                 jsonGenerator.writeString(SensitiveInfoUtils.address(s, 4)); 
  37.                 break; 
  38.             } 
  39.             case EMAIL: { 
  40.                 jsonGenerator.writeString(SensitiveInfoUtils.email(s)); 
  41.                 break; 
  42.             } 
  43.             case BANK_CARD: { 
  44.                 jsonGenerator.writeString(SensitiveInfoUtils.bankCard(s)); 
  45.                 break; 
  46.             } 
  47.             case CNAPS_CODE: { 
  48.                 jsonGenerator.writeString(SensitiveInfoUtils.cnapsCode(s)); 
  49.                 break; 
  50.             } 
  51.         } 
  52.     } 
  53.  
  54.     @Override 
  55.     public JsonSerializer createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { 
  56.         // 为空直接跳过 
  57.         if (beanProperty != null) { 
  58.             // 非 String 类直接跳过 
  59.             if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) { 
  60.                 SensitiveWrapped sensitiveWrapped = beanProperty.getAnnotation(SensitiveWrapped.class); 
  61.                 if (sensitiveWrapped == null) { 
  62.                     sensitiveWrapped = beanProperty.getContextAnnotation(SensitiveWrapped.class); 
  63.                 } 
  64.                 if (sensitiveWrapped != null) { 
  65.                     // 如果能得到注解,就将注解的 value 传入 SensitiveSerialize 
  66.                     return new SensitiveSerialize(sensitiveWrapped.value()); 
  67.                 } 
  68.             } 
  69.             return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty); 
  70.         } 
  71.         return serializerProvider.findNullValueSerializer(beanProperty); 
  72.     } 
  73.  
  74.     public SensitiveSerialize() {} 
  75.  
  76.     public SensitiveSerialize(final SensitiveEnum type) { 
  77.         this.type = type; 
  78.     } 

其中createContextual的作用是通过字段已知的上下文信息定制JsonSerializer对象。

2.5、编写脱敏工具类

  1. import org.apache.commons.lang3.StringUtils; 
  2.  
  3. public class SensitiveInfoUtils { 
  4.  
  5.      
  6.     public static String chineseName(final String fullName) { 
  7.         if (StringUtils.isBlank(fullName)) { 
  8.             return ""
  9.         } 
  10.         final String name = StringUtils.left(fullName, 1); 
  11.         return StringUtils.rightPad(name, StringUtils.length(fullName), "*"); 
  12.     } 
  13.  
  14.      
  15.     public static String chineseName(final String familyName, final String givenName) { 
  16.         if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) { 
  17.             return ""
  18.         } 
  19.         return chineseName(familyName + givenName); 
  20.     } 
  21.  
  22.      
  23.     public static String idCardNum(final String id) { 
  24.         if (StringUtils.isBlank(id)) { 
  25.             return ""
  26.         } 
  27.  
  28.         return StringUtils.left(id, 3).concat(StringUtils 
  29.                 .removeStart(StringUtils.leftPad(StringUtils.right(id, 4), StringUtils.length(id), "*"), 
  30.                         "***")); 
  31.     } 
  32.  
  33.      
  34.     public static String fixedPhone(final String num) { 
  35.         if (StringUtils.isBlank(num)) { 
  36.             return ""
  37.         } 
  38.         return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"); 
  39.     } 
  40.  
  41.      
  42.     public static String mobilePhone(final String num) { 
  43.         if (StringUtils.isBlank(num)) { 
  44.             return ""
  45.         } 
  46.         return StringUtils.left(num, 3).concat(StringUtils 
  47.                 .removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), 
  48.                         "***")); 
  49.  
  50.     } 
  51.  
  52.      
  53.     public static String address(final String address, final int sensitiveSize) { 
  54.         if (StringUtils.isBlank(address)) { 
  55.             return ""
  56.         } 
  57.         final int length = StringUtils.length(address); 
  58.         return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*"); 
  59.     } 
  60.  
  61.      
  62.     public static String email(final String email) { 
  63.         if (StringUtils.isBlank(email)) { 
  64.             return ""
  65.         } 
  66.         final int index = StringUtils.indexOf(email, "@"); 
  67.         if (index <= 1) { 
  68.             return email; 
  69.         } else { 
  70.             return StringUtils.rightPad(StringUtils.left(email, 1), index"*"
  71.                     .concat(StringUtils.mid(email, index, StringUtils.length(email))); 
  72.         } 
  73.     } 
  74.  
  75.      
  76.     public static String bankCard(final String cardNum) { 
  77.         if (StringUtils.isBlank(cardNum)) { 
  78.             return ""
  79.         } 
  80.         return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart( 
  81.                 StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), 
  82.                 "******")); 
  83.     } 
  84.  
  85.      
  86.     public static String cnapsCode(final String code) { 
  87.         if (StringUtils.isBlank(code)) { 
  88.             return ""
  89.         } 
  90.         return StringUtils.rightPad(StringUtils.left(code, 2), StringUtils.length(code), "*"); 
  91.     } 
  92.  

6、编写测试实体类

最后,我们编写一个实体类UserEntity,看看转换后的效果如何?

  1. public class UserEntity { 
  2.  
  3.      
  4.     private Long userId; 
  5.  
  6.      
  7.     private String name
  8.  
  9.      
  10.     @SensitiveWrapped(SensitiveEnum.MOBILE_PHONE) 
  11.     private String mobile; 
  12.  
  13.      
  14.     @SensitiveWrapped(SensitiveEnum.ID_CARD) 
  15.     private String idCard; 
  16.  
  17.      
  18.     private String sex; 
  19.  
  20.      
  21.     private int age; 
  22.  
  23.     //省略get、set... 

测试程序如下:

  1. public class SensitiveDemo { 
  2.  
  3.     public static void main(String[] args) throws JsonProcessingException { 
  4.         UserEntity userEntity = new UserEntity(); 
  5.         userEntity.setUserId(1l); 
  6.         userEntity.setName("张三"); 
  7.         userEntity.setMobile("18000000001"); 
  8.         userEntity.setIdCard("420117200001011000008888"); 
  9.         userEntity.setAge(20); 
  10.         userEntity.setSex("男"); 
  11.  
  12.         //通过jackson方式,将对象序列化成json字符串 
  13.         ObjectMapper objectMapper = new ObjectMapper(); 
  14.         System.out.println(objectMapper.writeValueAsString(userEntity)); 
  15.     } 

结果如下:

  1. {"userId":1,"name":"张三","mobile":"180****0001","idCard":"420*****************8888","sex":"男","age":20} 

很清晰的看到,转换结果成功!

如果你当前的项目是基于SpringMVC框架进行开发的,那么在对象返回的时候,框架会自动帮你采用jackson框架进行序列化。

  1. @RequestMapping("/hello"
  2. public UserEntity hello() { 
  3.     UserEntity userEntity = new UserEntity(); 
  4.     userEntity.setUserId(1l); 
  5.     userEntity.setName("张三"); 
  6.     userEntity.setMobile("18000000001"); 
  7.     userEntity.setIdCard("420117200001011000008888"); 
  8.     userEntity.setAge(20); 
  9.     userEntity.setSex("男"); 
  10.     return userEntity; 

请求网页http://127.0.0.1:8080/hello,结果如下:

三、小结

在实际的业务场景开发中,采用注解方式进行全局数据脱敏处理,可以有效的解决敏感数据隐私泄露的问题。

本文主要从实操层面对数据脱敏处理做了简单的介绍,可能有些网友还有更好的解决方案,欢迎下方留言,后面如果遇到了好的解决办法,也会分享给大家,愿对大家有所帮助!

四、参考

CSDN - 注解实现json序列化的时候自动进行数据脱敏

yanbin.blog - 自定义 Jackson 注解与禁用某一特定的注解

 

简书 - 数据脱敏处理

 

来源:Java极客技术内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯