脱敏工具
为防止隐私或敏感数据的泄露,项目开发中经常需要对特定的数据进行脱敏处理,BallCat 为此提供了一个脱敏工具包
依赖安装
此工具包已发布到 Maven 中央仓库,可以将以下内容添加到您的 POM 文件中以使用
<dependency>
<groupId>org.ballcat</groupId>
<artifactId>ballcat-desensitize</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
脱敏类型
根据不同的脱敏方式,BallCat 划分出了四种脱敏类型
Simple(简单脱敏)
只需根据原始数据即可推出脱敏后的数据的处理方式。
例如简单粗暴的将数据替换为 6个
*
号的处理方式:a1234
=>******
或者将 IPv4 后几位替换为*
:192.168.2.1
=>192.*.*.*
Regex (正则脱敏)
将原始数据根据正则表达式进行替换处理。
例如邮箱处理后第一个字符和'@'之后的原文显示,中间的显示为4个*:
12123124213@qq.com
=>1****@qq.com
Slide(滑动脱敏)
根据设置的左右明文数来控制明文展示,剩余的全部替换为 *,支持反转规则,好处在于会保留原始数据位数,提升辨析度。
例如身份证号处理,明文保持前6后4:
655356198812031234
=>655356********1234
,反转结果为******19881203****
Index(基于下标脱敏)
根据设置的index规则控制明文展示,剩余的全部替换为 *,支持反转规则。
例如指定下标为"3-6,8,10-"表示第4,5,6,7,9,11以及11之后的位替换处理:
43012319990101432X
=>430****9*9********
,反转规则则结果为***1231*9*0101432X
代码脱敏
基于 DesensitizationUtil
为方便使用,Ballcat 提供了工具类 DesensitizationUtil
,内置了一些常用的脱敏方法。
- 支持常用类型的脱敏:如姓名、身份证、银行卡号、手机号、密码、加密密文、座机、邮箱、地址、IP等
- 支持自定义前后保留多少位脱敏,可自定义脱敏占位符
- 支持基于自定义规则的脱敏:如指定"3-6,8,10-"表示第4,5,6,7,9,11以及11之后的位使用加密字符替换
姓名脱敏
中文姓名只显示第一个姓和最后一个汉字(单名则只显示最后一个汉字),其他隐藏为星号
DesensitizationUtil.maskChineseName("张梦") = "*梦"
DesensitizationUtil.maskChineseName("张小梦") = "张*梦"
身份证号脱敏
身份证(18位或者15位)显示前六位, 四位,其他隐藏。
DesensitizationUtil.maskIdCardNo("43012319990101432X") = "430123********432X"
手机号脱敏
移动电话前三位,后四位,其他隐藏
DesensitizationUtil.maskPhoneNumber("13812345678") = "138****5678"
地址脱敏
地址脱敏,只显示到地区,不显示详细地址
DesensitizationUtil.maskAddress("北京市西城区金城坊街2号") = "北京市西城区******"
邮箱脱敏
电子邮箱脱敏,邮箱前缀最多显示前1字母,前缀其他隐藏,用星号代替,@及后面的地址显示
DesensitizationUtil.maskEmail("test.demo@qq.com") = "t****@qq.com"
银行卡号脱敏
银行卡号脱敏,显示前六位后四位
DesensitizationUtil.maskBankCardNo("62226000000043211234") = "622260**********1234"
密码脱敏
密码脱敏,用******代替
DesensitizationUtil.maskPassword(password) = "******"
IP脱敏
IPv脱敏,支持IPv4和IPv6
DesensitizationUtil.maskIP("192.168.2.1") = "192.*.*.*"
DesensitizationUtil.maskIP("2001:0db8:02de:0000:0000:0000:0000:0e13") = "2001:*:*:*:*:*:*:*"
基于简单脱敏处理器脱敏
根据简单类型脱敏处理器脱敏。
DesensitizationUtil.maskBySimpleHandler(password, SixAsteriskDesensitizationHandler.class) = "******"
基于正则脱敏
根据RegexDesensitizeRule
正则脱敏规则脱敏
DesensitizationUtil.maskByRegex("test.demo@qq.com", new EmailRegexDesensitizeRule()) = "t****@qq.com"
基于正则脱敏(自定义正则)
自定义替换正则和替换模板。
DesensitizationUtil.maskByRegex("test.demo@qq.com", "(^.)[^@]*(@.*$)", "$1****$2") = "t****@qq.com"
基于滑动脱敏
根据SlideDesensitizeRule
滑动脱敏规则脱敏,支持反转
DesensitizationUtil.maskBySlide("655356198812031234", new IdCardNoSlideDesensitizeRule()) = "655356********1234"
DesensitizationUtil.maskBySlide("655356198812031234", new IdCardNoSlideDesensitizeRule(), true) = "******19881203****"
基于滑动脱敏(灵活参数)
自定义左边和右边长度以及替换字符。
DesensitizationUtil.maskBySlide("Hello World", 2, 3) = "He******rld"
DesensitizationUtil.maskBySlide("Hello World", 2, 3, true) = "**llo Wo***"
DesensitizationUtil.maskBySlide("Hello World", 2, 3, "#") = "He######rld"
基于下标脱敏
支持自定义index规则(集)脱敏
DesensitizationUtil.maskByIndex("43012319990101432X", "1", "4-6", "9-")) = "4*01***99*********"
DesensitizationUtil.maskByIndex("43012319990101432X", true, "1", "4-6", "9-")) = "4*01***99*********"
基于脱敏处理器
DesensitizationUtil 是在脱敏处理器之上做的一层封装,用户也可以直接使用脱敏处理器进行脱敏处理。
BallCat 提供了 DesensitizationHandlerHolder
类,来对系统内的所有脱敏处理器进行归集,方便使用的时候直接获取,而不必再创建新的对象。
简单脱敏
对于简单脱敏类型,BallCat 只内置了一下三种脱敏处理器:
SixAsteriskDesensitizationHandler
: 不管原文是什么,一律返回6个 *IPDesensitizationHandler
: 对IP地址进行脱敏处理PhoneNumberDesensitizationHandler
: 对手机号进行脱敏处理
提示
使用者可以定制自己的简单脱敏处理器,具体步骤请参看扩展使用一节。
使用示例:
// 获取简单脱敏处理器
SimpleDesensitizationHandler desensitizationHandler =
DesensitizationHandlerHolder.getSimpleDesensitizationHandler(SixAsteriskDesensitizationHandler.class);
String origin = "你好吗?"; // 原始字符串
String target = desensitizationHandler.mask(origin); // 替换处理
System.out.println(target); // 结果:******
SimpleDesensitizationHandler desensitizationHandler =
DesensitizationHandlerHolder.getSimpleDesensitizationHandler(IPDesensitizationHandler.class);
String origin = "192.168.2.1"; // 原始字符串
String target = desensitizationHandler.mask(origin); // 替换处理
System.out.println(target); // 结果:192.*.*.*
String origin = "2001:0db8:02de:0000:0000:0000:0000:0e13"; // 原始字符串
String target = desensitizationHandler.mask(origin); // 替换处理
System.out.println(target); // 结果:2001:*:*:*:*:*:*:*
正则脱敏
正则脱敏使用时,除了原始字符串之外,还需要提供正则表达式,以及占位替换表达式,以便处理数据。
使用示例:
// 获取正则脱敏处理器
RegexDesensitizationHandler desensitizationHandler =
DesensitizationHandlerHolder.getRegexDesensitizationHandler();
String origin = "12123124213@qq.com"; // 原始字符串
String regex = "(^.)[^@]*(@.*$)"; // 正则表达式
String replacement = "$1****$2"; // 占位替换表达式
String target = desensitizationHandler.mask(origin, regex, replacement); // 替换处理
System.out.println(target); // 结果:1****@qq.com
由于 BallCat 默认提供了 Email 类型的脱敏正则,所以可以使用以下代码来简化正则的定义:
// 使用内置的正则脱敏类型
String target2 = desensitizationHandler.mask(origin, RegexDesensitizationTypeEnum.EMAIL);
System.out.println(target2); // 结果:1****@qq.com
滑动脱敏
滑动脱敏则除了原始字符串之外,还需要提供左边和右边各自需要展示的明文数量,明文数量可以为 0。支持反转结果。
使用示例:
// 获取滑动脱敏处理器
SlideDesensitizationHandler desensitizationHandler =
DesensitizationHandlerHolder.getSlideDesensitizationHandler();
String origin = "15805516789"; // 原始字符串
String target1 = desensitizationHandler.mask(origin, 3, 2); // 替换处理
System.out.println(target1); // 结果:158******89
和正则脱敏一样,由于 BallCat 默认提供了 PhoneNumber 类型的滑动规则,所以可以使用以下代码来简化:
// 使用内置的滑动脱敏规则
String target2 = desensitizationHandler.mask(origin, SlideDesensitizationTypeEnum.PHONE_NUMBER);
System.out.println(target2); // 结果:158******89
基于下标脱敏
基于下标规则脱敏则除了原始字符串之外,还需要提供需要脱敏的index规则(集)。支持反转规则。
使用示例:
// 获取基于下标规则脱敏处理器
RuleDesensitizationHandler desensitizationHandler = DesensitizationHandlerHolder
.getRuleDesensitizationHandler();
String origin = "43012319990101432X"; // 原始字符串
String target1 = desensitizationHandler.mask(origin, "1", "4-6", "9-"); // 替换处理
System.out.println(target1); // 结果:4*01***99*********
String target2 = desensitizationHandler.mask(origin, true, "1", "4-6", "9-"); // 替换处理
System.out.println(target2); // 结果:*3**231**90101432X
注解脱敏
目前脱敏组件内置了基于 Jackson 的 脱敏处理。
注解分类
@SimpleDesensitize
:简单类型脱敏@RegexDesensitize
:正则类型脱敏@SlideDesensitize
:滑动类型脱敏@RuleDesensitize
:下标类型脱敏
注解示例
在需要进行脱敏处理的实体属性上添加对应的脱敏注解:
@Data
@Accessors(chain = true)
public class DesensitizationUser {
/**
* 用户名,不脱敏
*/
private String username;
/**
* 密码脱敏
*/
@RegexDesensitize(rule = EncryptedPasswordRegexDesensitizeRule.class)
private String password;
/**
* 邮件
*/
@RegexDesensitize(rule = EmailRegexDesensitizeRule.class)
private String email;
/**
* 手机号
*/
@SimpleDesensitize(handler = PhoneNumberDesensitizationHandler.class)
private String phoneNumber;
/**
* 测试自定义脱敏
*/
@SimpleDesensitize(handler = TestDesensitizationHandler.class)
private String testField;
/**
* 测试自定义注解脱敏
*/
@CustomerDesensitize(type = "自定义注解示例")
private String customDesensitize;
/**
* 测试规则脱敏
*/
@IndexDesensitize(rule = { "1", "4-6", "9-" })
private String ruleDesensitize;
/**
* 测试规则脱敏(反转)
*/
@IndexDesensitize(rule = { "1", "4-6", "9-" }, reverse = true)
private String ruleReverseDesensitize;
}
Json 脱敏
目前脱敏组件仅内置了基于 Jackson 的 JSON 脱敏支持.
定义 Json 序列化修改器
必须先将 JsonSerializerModifier
注册到 Jackson 的 ObjectMapper
中,示例代码如下:
// 1.创建 ObjectMapper 对象
ObjectMapper objectMapper = new ObjectMapper();
// 2.实例化 JsonSerializerModifier
JsonSerializerModifier modifier = new JsonSerializerModifier();
// 3.将自定义序列化构建器 注册进 ObjectMapper
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(modifier));
如果是 Spring Boot 项目,推荐使用以下方式进行 JSON 脱敏序列化修改器的注册。
// 注册 Jackson 的脱敏模块
@Bean
public JsonDesensitizeModule jsonDesensitizeModule() {
JsonDesensitizeSerializerModifier desensitizeModifier = new JsonDesensitizeSerializerModifier();
return new JsonDesensitizeModule(desensitizeModifier);
}
使用示例
提示
Spring 项目可以直接通过注入的方式获取 ObjectMapper 实例。
void test(){
DesensitizationUser user = new DesensitizationUser()
.setEmail("chengbohua@foxmail.com")
.setUsername("xiaoming")
.setPassword("admina123456")
.setPhoneNumber("15800000000")
.setTestField("这是测试属性")
.setRuleDesensitize("43012319990101432X")
.setRuleReverseDesensitize("43012319990101432X");;
String value = objectMapper.writeValueAsString(user);
Assert.isTrue("{\"username\":\"xiaoming\",\"password\":\"adm****56\",\"email\":\"c****@foxmail.com\",\"phoneNumber\":\"158******00\",\"testField\":\"TEST-这是测试属性\",\"ruleDesensitize\":\"4*01***99*********\",\"ruleReverseDesensitize\":\"*3**231**90101432X\"}"
.equals(value));
}
Web 脱敏
提示
仅在 SpringMvc(SpringBoot) 项目中生效,需要参照 Json 脱敏 中的使用方法,注册 JsonSerializerModifier 到 ObjectMapper 中。
由于 SpringMvc(SpringBoot)默认使用 Jackson 的 HttpMessageConverter 实现作为 Content-Type 为 application/json 时的响应消息处理器,所以在 Controller 类上添加了 @RestController
注解或者方法上添加了 @ResponseBody
注解时,会自动对响应实体进行脱敏处理。
Excel 脱敏
Ballcat 提供了 EasyExcel 对于脱敏注解的处理实现,具体使用参看 Excel 组件 文档。
扩展使用
扩展脱敏规则
组件内置的规则无法实现业务需求时,可以通过实现 SlideDesensitizeRule
或 RegexDesensitizeRule
接口来对应扩展滑动脱敏或正则脱敏规则。
例如实现自己的银行卡号滑动脱敏规则,前7后3为明文,其他位替换为 *。
public class MyBankCardNoSlideDesensitizeRule implements SlideDesensitizeRule {
@Override
public int leftPlainTextLen() {
return 7;
}
@Override
public int rightPlainTextLen() {
return 3;
}
@Override
public String maskString() {
return "*";
}
@Override
public boolean reverse() {
return false;
}
}
扩展简单类型脱敏处理器
a) 首先定义自己的 SimpleDesensitizationHandler
实现类:
public class SimpleDesensitizatioHanderSPIExample implements SimpleDesensitizationHandler {
@Override
public String handle(String s) {
return "------";
}
}
b) BallCat 会利用 java 的 SPI 机制来加载简单脱敏处理器
所以只需在项目的 resources/META-INF/services
目录下新建名为
org.ballcat.desensitize.handler.SimpleDesensitizationHandler
的文件。
文件内容为自定义的脱敏处理器的全类名,多个实现则每个实现类名单独一行。
c) 使用示例
获取自定义处理器,入参为该处理器类目
// 获取简单脱敏处理器
SimpleDesensitizationHandler desensitizationHandler =
DesensitizationHandlerHolder.getSimpleDesensitizationHandler(SimpleDesensitizatioHanderSPIExample.class);
配合 JSON 注解使用时,只需指定 Handler 类型为该类即可
/**
* 测试自定义脱敏
*/
@SimpleDesensitize(handler = SimpleDesensitizatioHanderSPIExample.class)
private String testField;
脱敏策略
JSON 处理时,根据某些逻辑判断是否进行脱敏,需要用户进行脱敏策略接口的实现:
public interface DesensitizeStrategy {
/**
* 判断是否忽略字段
* @param fieldName {@code 当前字段名称}
* @return @{code true 忽略 |false 不忽略}
*/
boolean ignoreField(String fieldName);
}
在注册脱敏修改器的时候,进行
// 自定义策略,当前用户是管理员时忽略 phoneNumber 字段的脱敏处理
DesensitizeStrategy strategy = (fieldName) -> {
return fieldName.equals("phoneNumber") && isAdmin;
};
JsonSerializerModifier modifier = new JsonSerializerModifier(strategy);
自定义脱敏注解示例
新增自定义注解
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomerDesensitize {
/**
* 类型字段
* @return type
*/
String type();
}
注册自定义脱敏类型处理器
//实现自定义脱敏处理器
CustomDesensitisedHandler customDesensitisedHandler = new CustomDesensitisedHandler();
//将自定义脱敏处理器绑定
DesensitizationHandlerHolder.addDesensitizationHandler(CustomDesensitisedHandler.class, customDesensitisedHandler);
注册注解处理器
//注册注解 处理器
AnnotationHandlerHolder.addHandleFunction(CustomerDesensitize.class, (annotation, value) -> {
CustomerDesensitize customerDesensitize= (CustomerDesensitize) annotation;
String type = customerDesensitize.type();
log.info("注解上的参数{}",type);
CustomDesensitisedHandler handler = (CustomDesensitisedHandler) DesensitizationHandlerHolder
.getDesensitizationHandler(CustomDesensitisedHandler.class);
return handler.mask(value);
});
在实体字段上指定自定义注解
@CustomerDesensitize(type = "自定义注解")
private String customDesensitize;