数据脱敏 – 正则表达式
上节的内容我们说到了使用工具类对要出输出的数据进行掩码处理以达到保护数据的目的,上面的方法在一些简单场景都好使用,但是遇到类似最后提出的json字符串处理起来就 力有不逮 了。这时就需要用到一种新的字符串处理技术 – 没错,它就是正则表达式。
关于正则表达式的基础,前面的花过几节内容做了比较相信的讲解,感兴趣的可以前去考古,重学正则表达式。
怎么用?
正则表达式
对于上一节提到的复杂字符串的匹配,可以使用正则表达式来处理,例如,手机号的正则是这样 (不考虑号段等问题,只认为手机号是一个11位的以 1 开头的数字字符串)
^1\d{2}\d{4}\d{4}$
因为需要对手机号中间部分进行替换,这里需要用到一个分组的概念,因此手机号的正则表达式加入分组以后是这个样子:
(1\d{2})\d{4}(\d{4})
有了分组以后,我们只需要保留第一个分组和第二个分组,把其余部分替换为 * 就可以了,现在,修改下我们上一节的方法:
/**
* 手机号掩码处理 15312345567 --> 153****5567
*
* @param mobile 手机号
* @return 处理后的手机号
*/
public static String maskMobile(String mobile) {
if (!StringUtils.hasText(mobile)) {
return "";
}
return mobile.replaceAll("(1\\d{2})\\d{4}(\\d{4})","$1****$2");
}
测试一下:
public static void main(String[] args) {
String phone = "15312345567";
String maskMobile = maskMobile(phone);
log.info("maskMobile = {}", maskMobile);
}
// [com.info.examples.log.controller.MaskUtils:62] maskMobile = 153****5567
我们看到,结果依然是符合预期的,这时我们把刚才说的 json 字符串拿来进行测试:
public static void main(String[] args) {
String json = "{\"email\":\"10000@qq.com\",\"idNo\":\"31033219990909432X\",\"mobile\":\"15312345567\",\"name\":\"王语嫣\"}";
String maskJson = maskMobile(json);
log.info("maskJson = {}", maskJson);
}
// [com.info.examples.log.controller.MaskUtils:64] maskJson = {"email":"10000@qq.com","idNo":"3103****990909432X","mobile":"153****5567","name":"王语嫣"}
可以发现,手机号确实是已经被掩码处理了,但是身份证号也被处理了,而且处理的结果和我们预期不符,这是什么原因呢?
这里就是提到正则表达式的匹配机制了,仔细观察我们发现,其实身份证的 10332199909
这一部分数据也是满足我们上面写的手机号的规则的,因此这一部分数据也被匹配到处理了。这时怎么处理呢?我们要精确匹配手机号。通常我们是使用 ^
表示正则达表示的开始,使用 $
表示正则表达式的结束,修改正则表达式:
^(1\d{2})\d{4}(\d{4})$
在此测试,我们发现结果如下:
[com.info.examples.log.controller.MaskUtils:64] maskJson = {"email":"10000@qq.com","idNo":"31033219990909432X","mobile":"15312345567","name":"王语嫣"}
怎么越改越不对了呢?这次手机号也没有匹配到!还是回到正则表达式,我们说了 ^
表示正则达表示的开始,使用 $
表示正则表达式的结束,因此 ^
的前面不能出现任何字符,空格也不可以,$
的后面也不能出现任何字符。但是在 json 字符串里,手机号的前面还有 "
,结束后也会有 "
,因此我们上面的正则表达式是不满足 json 串中手机号的规则的,在此修改手机号的正则:
\"(1\d{2})\d{4}(\d{4})\"
这个时候再来测试:
[com.info.examples.log.controller.MaskUtils:64] maskJson = {"email":"10000@qq.com","idNo":"31033219990909432X","mobile":153****5567,"name":"王语嫣"}
只有手机号被掩码处理了,这就符合我们的预期了!
对正则表达式不理解的可以考古,重学正则表达式。
照猫画虎,修改其余的几个正则表达式:
@Slf4j
public class MaskUtils {
/**
* 手机号掩码处理 15312345567 --> 153****5567
*
* @param mobile 手机号
* @return 处理后的手机号
*/
public static String maskMobile(String mobile) {
if (!StringUtils.hasText(mobile)) {
return "";
}
return mobile.replaceAll("\"(1\\d{2})\\d{4}(\\d{4})\"", "$1****$2");
}
/**
* 姓名掩码处理 王语嫣 --> 王**
*
* @param name 姓名
* @return 处理后的姓名
*/
public static String maskName(String name) {
if (!StringUtils.hasText(name)) {
return "";
}
return name.replaceAll("['\"]([\u4e00-\u9fa5\\s])([\u4e00-\u9fa5.\\s]*)['\"]", "\"$1**\"");
}
/**
* 身份证号掩码处理 31033219990909432X --> 3103**********432X
*
* @param idCardNo 身份证号码
* @return 处理后的身份证号码
*/
public static String maskIdCardNo(String idCardNo) {
if (!StringUtils.hasText(idCardNo)) {
return "";
}
// 处理 15 位身份证
return idCardNo.replaceAll("['\"]([1-9]\\d{3})(\\d{7})(\\d{4})['\"]", "\"$1**********$3\"").
// 处理 18 位身份证
replaceAll("['\"]([1-9]\\d{3})(\\d{10}(\\d{3}[Xx]))['\"]", "\"$1**********$3\"");
}
/**
* 电子邮箱掩码处理 10000@qq.com --> 100***@qq.com
*
* @param email 电子邮箱
* @return 处理后的电子邮箱
*/
public static String maskEmail(String email) {
if (!StringUtils.hasText(email)) {
return "";
}
return email.replaceAll("['\"]([a-zA-Z0-9_\\-.]{1,3})[a-zA-Z0-9_\\-.]*@([a-zA-Z0-9_\\-.]+)\\.([a-zA-Z]{2,5})['\"]", "\"$1***@$2.$3\"");
}
}
再次测试:
public static void main(String[] args) {
String json = "{\"email\":\"10000@qq.com\",\"idNo\":\"31033219990909432X\",\"mobile\":\"15312345567\",\"name\":\"王语嫣\"}";
String maskJson = maskEmail(maskIdCardNo(maskName(maskMobile(json))));
log.info("maskJson = {}", maskJson);
}
// [com.info.examples.log.controller.MaskUtils:73] maskJson = {"email":"100***@qq.com","idNo":"3103**********432X","mobile":153****5567,"name":"王**"}
我们发现,所有的敏感信息都按我们的规则进行了掩码处理。这时候我们只需要封装一个处理 json 掩码的方法,打印 json 前调用一下这个方法就可以了。
但是事情真的那么简单吗?
但是现实往往是残酷的,如果是一个新项目,确实可以这么做,但是如果要是对一个老项目进行改造呢?每个打印日志的方法都去改一遍吗?
那么有没有一个更简单的方法呢?
答案是 有!
今天的内容到这里暂告一段落,谢谢各位阅读,如有不妥之处还请指正,感激不尽。
欲知后事如何,且听下回分解。