数据脱敏的应用场景主要有2类:
- 接口返回数据
- 日志打印数据
针对上述场景的需求,数据脱敏的实现方法可以有如下3种:
- 基于SQL进行脱敏,保证查询到的结果就是脱敏信息:SQL需要精心设计,依赖数据库函数,性能不高。
- 应用层脱敏:将查询到数据根据一定的策略进行脱敏后再返回或打印日志,对应用代码有入侵,性能可控。
- 基于日志框架进行脱敏:使用正则表达式进行处理,对日志格式有要求,否则无法识别需要脱敏的信息。
基于SQL语句脱敏
基于SQL语句的数据脱敏,需要依赖相应的数据库函数。
如下,以MySQL数据库为例,在查询数据的时候通过函数concat()
,left()
和right()
实现脱敏处理。
-- 通过SQL语句对查询结果进行脱敏处理
select
concat(left(mobile,3),'********') as mobile_desensitive,
concat(left(idcard,5),'*********',right(idcard,4)) as idcard_desensitive
from `user` u;
查询结果如下:
mobile_desensitive|idcard_desensitive|
------------------+------------------+
132******** |53211*********1111|
153******** |11011*********1111|
基于SQL语句的数据脱敏处理,SQL语句需要精心设计,并且需要依赖数据库函数,同时也可能存在性能瓶颈。
在数据规模可控时可以使用,否则建议使用其他方案。
应用层脱敏
数据脱敏的处理完全在应用层处理,也就说:什么数据该脱敏,如何脱敏,脱敏后如何使用完全在应用层控制。
在应用层进行数据脱敏可以应对日志打印,API接口数据返回等场景需求。
组件sensitive-plus提供了完整的解决方案。
字符串脱敏
// 调用工具方法对字符串进行脱敏
String mobile = "15678900987";
String sensitive = SensitiveInfoUtils.mobilePhone(mobile);
System.out.println(String.format("%s\n%s", source, sensitive));
输出:
15678900987
156****0987
对象属性脱敏
// 调用工具方法实现对象属性脱敏
String chineseName = "赵子龙";
String mobile = "13242429876";
String fixPhone = "010-32342214";
String address = "西川成都蜀国大将军府";
String idCard = "123456789012345678";
String bankCard = "6666666666666666666";
String email = "[email protected]";
String password = "1234567890";
SimpleEntity entity = SimpleEntity.builder()
.chineseName(chineseName)
.mobile(mobile)
.fixPhone(fixPhone)
.address(address)
.idCard(idCard)
.bankCard(bankCard)
.email(email)
.password(password)
.build();
Object desensitive = SensitiveInfoUtils.desensitive(entity);
System.out.println(JSON.toJSONString(desensitive));
输出:
{
"chineseName": "赵**",
"mobile": "132****9876",
"fixPhone": "********2214",
"address": "西川成都******",
"idCard": "**************5678",
"bankCard": "666666*********6666",
"email": "z*********@chengdu.com",
"password": "******"
}
或者直接调用JSON脱敏序列化方法:
System.out.println(SensitiveJsonUtils.toJson(entity));
注:该方式需要在对象实体上应用脱敏注解。
public class SimpleEntity {
/** 中文姓名 */
@SensitiveLengthChineseName
String chineseName;
/** 手机号 */
@SensitiveLengthMobile
String mobile;
/** 固定电话 */
@SensitiveLengthFixedPhone
String fixPhone;
/** 地址 */
@SensitiveLengthAddress
String address;
/** 身份证号 */
@SensitiveLengthIdCard
String idCard;
/** 银行卡号 */
@SensitiveLengthBankCard
String bankCard;
/** 邮箱 */
@SensitiveLengthEmail
String email;
/** 密码 */
@SensitiveLengthPassword
String password;
}
基于日志框架脱敏
在Logback框架中,可以通过自定义MessageConverter
的方式实现对日志消息脱敏处理。
具体实现可以参考sensitive-plus日志脱敏。
这种基于日志框架的脱敏方式,有一些局限性:
- 只能应用在日志脱敏场景
- 基于正则式处理,对日志格式有要求
- 存在性能瓶颈
请谨慎使用!
写在最后
综上,不论是日志打印还是接口返回数据场景的脱敏需求,出于灵活性和性能考虑,都应该优先考虑在应用层处理。
至于在应用层处理的侵入性,可以通过项目约定进行统一即可。
【参考】
数据脱敏的 3 种常见方案,好用到爆!
logback-defender实现日志脱敏
使用Logback脱敏-扩展篇
基于logback的日志“规范”和“脱敏”