应用场景

数据库中密文存储身份证、手机号等敏感信息时,Java需要将密文数据转换为明文并脱敏返回给前端。

脱敏方式枚举类

public enum DesensitizationTypeEnum {/*** 默认方式*/DEFAULT,/*** 头部脱敏*/HEAD,/*** 尾部脱敏*/TAIL,/*** 中间脱敏*/MIDDLE,/*** 头尾脱敏*/HEAD_TAIL,/*** 全部脱敏*/ALL,/*** 不脱敏,相当于没打这个注解*/NONE;
}

脱敏数据类型枚举类

@Getter
@NoArgsConstructor
public enum DesensitizationDataTypeEnum {/*** 用户ID*/USER_ID,/*** 中文名*/CHINESE_NAME,/*** 身份证*/ID_CARD,/*** 座机号*/FIXED_PHONE,/*** 手机号*/MOBILE_PHONE,/*** 地址*/ADDRESS,/*** 电子邮件*/EMAIL,/*** 密码*/PASSWORD,/*** 车牌*/CAR_LICENSE,/*** 银行卡号*/BANK_CARD,/*** 其他*/OTHER;}

脱敏工具类

public class DesensitizationUtil {public DesensitizationUtil() {}public static String desensitized(CharSequence str, DesensitizationDataTypeEnum desensitizedType) {if (StrUtil.isBlank(str)) {return "";} else {String newStr = String.valueOf(str);switch (desensitizedType.ordinal()) {case 1:newStr = String.valueOf(userId());break;case 2:newStr = chineseName(String.valueOf(str));break;case 3:newStr = idCardNum(String.valueOf(str), 1, 2);break;case 4:newStr = fixedPhone(String.valueOf(str));break;case 5:newStr = mobilePhone(String.valueOf(str));break;case 6:newStr = address(String.valueOf(str));break;case 7:newStr = email(String.valueOf(str));break;case 8:newStr = password(String.valueOf(str));break;case 9:newStr = carLicense(String.valueOf(str));break;case 10:newStr = bankCard(String.valueOf(str));}return newStr;}}public static Long userId() {return 0L;}/*** 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**** @param fullName 姓名* @return 脱敏后的姓名*/public static String chineseName(String fullName) {return StrUtil.isBlank(fullName) ? "" : StrUtil.hide(fullName, 1, fullName.length());}/*** 【身份证号】显示前1位和后2位,其他隐藏。共计18位或者15位,比如:1*****************1** @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 "";} else if (front + end > idCardNum.length()) {return "";} else {return front >= 0 && end >= 0 ? StrUtil.hide(idCardNum, front, idCardNum.length() - end) : "";}}/*** 【固定电话】 前四位,后两位** @param num 固定电话* @return 脱敏后的固定电话*/public static String fixedPhone(String num) {return StrUtil.isBlank(num) ? "" : StrUtil.hide(num, 4, num.length() - 2);}/*** 【手机号码】前三位,后四位,其他隐藏,比如:138******1234** @param num 手机号码* @return 脱敏后的手机号码*/public static String mobilePhone(String num) {return StrUtil.isBlank(num) ? "" : StrUtil.hide(num, 3, num.length() - 4);}/*** 【地址】只显示到地区的三分之二,尾部脱敏,不显示详细地址,比如:北京市海淀区****** @param address 地址* @return 脱敏后的地址*/public static String address(String address) {if (StrUtil.isBlank(address)) {return "";} else {// 地址尾部脱敏三分之一int length = address.length();int end = length / 3;return StrUtil.hide(address, length - end, length);}}/*** 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com** @param email 邮箱* @return 脱敏后的邮箱*/public static String email(String email) {if (StrUtil.isBlank(email)) {return "";} else {int index = StrUtil.indexOf(email, '@');return index <= 1 ? email : StrUtil.hide(email, 1, index);}}/*** 【密码】密码的全部字符都用*代替,比如:******** @param password 密码* @return 脱敏后的密码*/public static String password(String password) {return StrUtil.isBlank(password) ? "" : 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 "";} else {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 脱敏后的银行卡号*/public static String bankCard(String bankCardNo) {if (StrUtil.isBlank(bankCardNo)) {return bankCardNo;} else {bankCardNo = StrUtil.trim(bankCardNo);if (bankCardNo.length() < 9) {return bankCardNo;} else {int length = bankCardNo.length();int midLength = length - 8;StringBuilder buf = new StringBuilder();buf.append(bankCardNo, 0, 4);for (int i = 0; i < midLength; ++i) {if (i % 4 == 0) {buf.append(' ');}buf.append('*');}buf.append(' ').append(bankCardNo, length - 4, length);return buf.toString();}}}
}

脱敏注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerializer.class)
public @interface Desensitization {/*** 脱敏数据类型** @return*/DesensitizationDataTypeEnum dataType();/*** 是否解密,默认false*/boolean decrypt() default false;/*** 脱敏类型** @return*/DesensitizationTypeEnum type() default DesensitizationTypeEnum.DEFAULT;/*** 尾部不脱敏的长度,默认1,当type为HEAD或HEAD_TAIL时使用*/int tailNoMaskLen() default 1;/*** 头部不脱敏的长度,默认1,当type为TAIL或HEAD_TAIL时使用*/int headNoMaskLen() default 1;/*** 中间不脱敏的长度,默认1,当type为MIDDLE时使用*/int middleNoMaskLen() default 1;/*** 脱敏字符,默认**/char maskCode() default '*';}

脱敏序列化器

@NoArgsConstructor
public class DesensitizationSerializer extends JsonSerializer<String> implements ContextualSerializer {private Desensitization desensitization;public DesensitizationSerializer(Desensitization desensitization) {this.desensitization = desensitization;}@Overridepublic void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {jsonGenerator.writeString(desensitize(s));}@Overridepublic JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {if (beanProperty != null) {if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);if (desensitization == null) {desensitization = beanProperty.getContextAnnotation(Desensitization.class);}if (desensitization != null) {return new DesensitizationSerializer(desensitization);}}return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);}return serializerProvider.findNullValueSerializer(null);}/*** 脱敏处理*/private String desensitize(String s) {if (StrUtil.isNotBlank(s)) {DesensitizationDataTypeEnum dataType = desensitization.dataType();DesensitizationTypeEnum type = desensitization.type();// 解密处理boolean decrypt = desensitization.decrypt();if (decrypt) {String value = PasswordUtil.decrypt(s);// 正则匹配,如果匹配到身份证或手机号,则返回解密后的值,否则返回原值if (ReUtil.contains(ReConstant.ID_CARD, value) || ReUtil.contains(ReConstant.MOBILE, value)) {s = value;}}switch (type) {case DEFAULT:// 默认方式,根据dataType自动选择脱敏方式s = autoDesensitize(s, dataType);break;case HEAD:// 头部脱敏s = headDesensitize(s);break;case TAIL:// 尾部脱敏s = tailDesensitize(s);break;case MIDDLE:s = middleDesensitize(s);break;case HEAD_TAIL:s = headTailDesensitize(s);break;case ALL:s = allDesensitize(s);break;case NONE:// 不做脱敏break;default:}}return s;}/*** 全部脱敏*/private String allDesensitize(String s) {return StrUtil.fillBefore(s, desensitization.maskCode(), s.length());}/*** 头尾脱敏*/private String headTailDesensitize(String s) {int middleNoMaskLen = desensitization.middleNoMaskLen();if (middleNoMaskLen >= s.length()) {// 如果中间不脱敏的长度大于等于字符串的长度,不进行脱敏return s;}int len = s.length() - middleNoMaskLen;// 头部脱敏int headStart = 0;int headEnd = len / 2;s = StrUtil.replace(s, headStart, headEnd, desensitization.maskCode());// 尾部脱敏int tailStart = s.length() - (len - len / 2);int tailEnd = s.length();return StrUtil.replace(s, tailStart, tailEnd, desensitization.maskCode());}/*** 中间脱敏*/private String middleDesensitize(String s) {int headNoMaskLen = desensitization.headNoMaskLen();int tailNoMaskLen = desensitization.tailNoMaskLen();if (headNoMaskLen + tailNoMaskLen >= s.length()) {// 如果头部不脱敏的长度+尾部不脱敏长度 大于等于字符串的长度,不进行脱敏return s;}int start = headNoMaskLen;int end = s.length() - tailNoMaskLen;return StrUtil.replace(s, start, end, desensitization.maskCode());}/*** 尾部脱敏*/private String tailDesensitize(String s) {int headNoMaskLen = desensitization.headNoMaskLen();if (headNoMaskLen >= s.length()) {// 如果头部不脱敏的长度大于等于字符串的长度,不进行脱敏return s;}int start = headNoMaskLen;int end = s.length();return StrUtil.replace(s, start, end, desensitization.maskCode());}/*** 头部脱敏*/private String headDesensitize(String s) {int tailNoMaskLen = desensitization.tailNoMaskLen();if (tailNoMaskLen >= s.length()) {// 如果尾部不脱敏的长度大于等于字符串的长度,不进行脱敏return s;}int start = 0;int end = s.length() - tailNoMaskLen;return StrUtil.replace(s, start, end, desensitization.maskCode());}/*** 根据数据类型自动脱敏*/private String autoDesensitize(String s, DesensitizationDataTypeEnum dataType) {switch (dataType) {case CHINESE_NAME:s = DesensitizationUtil.chineseName(s);break;case FIXED_PHONE:s = DesensitizationUtil.fixedPhone(s);break;case MOBILE_PHONE:s = DesensitizationUtil.mobilePhone(s);break;case ADDRESS:s = DesensitizationUtil.address(s);break;case PASSWORD:s = DesensitizationUtil.password(s);break;case BANK_CARD:s = DesensitizationUtil.bankCard(s);break;case EMAIL:s = DesensitizationUtil.email(s);break;case ID_CARD:s = DesensitizationUtil.idCardNum(s, 6, 4);break;case OTHER:// 其他类型的不支持以默认方式脱敏,直接返回break;default:}return s;}
}

返回对象

@ApiModelProperty(value = "证件号码")@Desensitization(dataType = DesensitizationDataTypeEnum.ID_CARD, decrypt = true)@Encrypt // 前端传参时对数据进行加密匹配数据库密文身份证private String idcard;@ApiModelProperty(value = "联系电话: 一般登记手机密码,将在找回密码、修改密码时用于发送验证短信。")@Desensitization(dataType = DesensitizationDataTypeEnum.MOBILE_PHONE, decrypt = true)@Encryptprivate String tel;@ApiModelProperty(value = "住址")@Desensitization(dataType = DesensitizationDataTypeEnum.ADDRESS)private String address;

Java自定义数据脱敏注解相关推荐

  1. Java实现数据脱敏

    前言   在开发系统过程中,经常会接触到大量的数据信息,这些数据信息可能包含身份证号.手机号.姓名.卡号等各种敏感信息.而有些用户需求是不允许,这些敏感信息数据为了保护用户个人信息的安全.对这些数据需 ...

  2. java web 数据脱敏

    java web 数据脱敏 参考上文: java日志脱敏实现 1. 题记 在交易管理系统中,由于数据库存储客户人脸图片和客户名称.客户证件号.手机号.银行卡号等相关敏感字段,为了防止数据泄露现根据用户 ...

  3. java 自定义json解析注解 复杂json解析 工具类

    java 自定义json解析注解 复杂json解析 工具类 目录 java 自定义json解析注解 复杂json解析 工具类 1.背景 2.需求-各式各样的json 一.一星难度json[json对象 ...

  4. 聊聊如何自定义数据脱敏

    前言 什么是数据脱敏 数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护 常用脱敏规则 替换.重排.加密.截断.掩码 良好的数据脱敏实施 1.尽可能地为脱敏后的应用,保留 ...

  5. Java 实现数据脱敏的技术方案

    数据脱敏是保护个人隐私的一种重要手段,它通过对敏感信息进行处理,将敏感信息转换为不敏感的信息,以保护个人隐私不被泄漏.在Java中,数据脱敏也是一项非常重要的技术,本文将从数据脱敏的概念.Java中的 ...

  6. java 数据库数据脱敏_Sharding-JDBC-数据脱敏

    数据脱敏 该章节主要介绍如何使用数据脱敏功能,如何进行相关配置.数据脱敏功能即可与数据分片功能共同使用,又可作为单独功能组件,独立使用. 与数据分片功能共同使用时,会创建ShardingDataSou ...

  7. 一文玩转 Java 日志数据脱敏,就是如此简单!

    自定义Layout 编写log4j配置 正则匹配说明 注意事项 脱敏测试 许多系统为了安全需要对敏感信息(如手机号.邮箱.姓名.身份证号.密码.卡号.住址等)的日志打印要求脱敏后才能输出,本文将结合个 ...

  8. java 自定义AfterReturning切面注解

    自定义一个注解并实现注解返回后处理逻辑功能 自定义一个注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Docum ...

  9. 一文玩转 Java 日志数据脱敏

最新文章

  1. 家用机器人风口来临,但巨头围猎背后的前景不容乐观
  2. 【正一专栏】内马尔要走快走、走好不送!
  3. 【PC工具】更新百度网盘高速下载工具——亿寻使用方法及注意事项
  4. 【存储知识学习】第十章- 存储架构演进过程《大话存储》阅读笔记
  5. ExtJs学习准备工作(二) firebug firefox插件的安装
  6. c语言double字母,C语言double和float 实例分析
  7. mysql 临时表 heap_mysql优化: 内存表和临时表
  8. (转)Web Services使用多态(XmlInclude) ,支持自定义类型
  9. 我用Python帮朋友做了张猪肉数据分析图,结果。。。
  10. 06_LR和最大熵模型_统计学习方法
  11. iOS开发之touchesCancelled
  12. px2rem-loader(Vue:移动端自适应,px自动转化)
  13. 用windows开发ios app_开发一个APP至少需要多少人|安卓|ios|产品经理
  14. Web of science(WOS)引文跟踪
  15. ubuntu-键盘映射
  16. 大数据趣味学习探讨(三):怎么确定学习目标
  17. mac安装搜狗输入法
  18. 如何将txt、excel文档里面的电话号码快速转换为vcf格式的电话簿导入手机
  19. 基督里一切的丰盛_司布真
  20. 你不得不看的“互联网+企业购”大趴攻略

热门文章

  1. Xcode抓包ios
  2. androidd时光轴效果实现
  3. android底部菜单栏demo
  4. 把excel导入sqlserver外部表不是预期格式
  5. ysoserial Java反序列化漏洞利用实践
  6. 【Linux】Linux中rz和sz命令用法详解
  7. 2006信息技术会考复习(理论复习)
  8. 左右互搏:GAN在爱奇艺短视频推荐冷启动中的实践
  9. oracle自动备份教程,Oracle数据库自动备份脚本分享(超实用)
  10. 数仓工具—Hive实战之累计计算(7)