日志脱敏是常见的安全需求。普通的基于工具类方法的方式,对代码的入侵性太强。编写起来又特别麻烦。
本项目提供基于注解的方式,并且内置了常见的脱敏方式,便于开发。
用户也可以基于自己的实际需要,自定义注解。
为了金融交易的安全性,国家强制规定对于以下信息是要日志脱敏的:
-
用户名
-
手机号
-
邮箱
-
银行卡号
-
密码
-
身份证号
存储的时候上面的信息都需要加密,密码为不可逆加密,其他为可逆加密。
类似的功能有很多。不在本系统的解决范围内。
-
基于注解的日志脱敏。
-
可以自定义策略实现,策略生效条件。
-
常见的脱敏内置方案。
-
java 深拷贝,且原始对象不用实现任何接口。
-
支持用户自定义注解。
-
支持基于 FastJSON 直接生成脱敏后的 json
- 内置身份证号脱敏脱敏策略
JDK 7+
Maven 3.x
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>sensitive-core</artifactId>
<version>0.0.15</version>
</dependency>
v0.0.15 放开基本工具类,可以只使用用工具方法。满足更灵活的场景,比如重写 toString() 等。
SensitiveStrategyUtil
中内置了几种脱敏工具方法,如下:
序号 | 方法 | 说明 | 入参例子 | 出参例子 |
---|---|---|---|---|
1 | password | 密码 | 123456 | null |
2 | chineseName | 中文姓名 | 张三丰 | 张*丰 |
3 | phone | 手机号 | 13012347894 | 130****7894 |
4 | 邮箱 | [email protected] | 123***@gmail.com |
|
5 | cardId | 卡号 | 1234888888888888884321 | 123488**********884321 |
6 | idNo | 身份证号 | 130701199310308888 | 130*************88 |
SensitiveUtil
工具类的核心方法列表如下:
序号 | 方法 | 参数 | 结果 | 说明 |
---|---|---|---|---|
1 | desCopy() | 目标对象 | 深度拷贝脱敏对象 | 适应性更强 |
2 | desJson() | 目标对象 | 脱敏对象 json | 性能较好 |
3 | desCopyCollection() | 目标对象集合 | 深度拷贝脱敏对象集合 | |
4 | desJsonCollection() | 目标对象集合 | 脱敏对象 json 集合 |
- UserIdNo.java
我们对 password 使用脱敏,指定脱敏策略为 StrategyPassword。(直接返回 null)
public class UserIdNo {
@Sensitive(strategy = StrategyChineseName.class)
private String username;
@Sensitive(strategy = StrategyCardId.class)
private String idCard;
@Sensitive(strategy = StrategyPassword.class)
private String password;
@Sensitive(strategy = StrategyEmail.class)
private String email;
@Sensitive(strategy = StrategyPhone.class)
private String phone;
@Sensitive(strategy = StrategyIdNo.class)
private String idNo;
//Getter & Setter
//toString()
}
- 数据准备
构建一个最简单的测试对象:
public static UserIdNo buildUserIdNo() {
UserIdNo user = new UserIdNo();
user.setUsername("脱敏君");
user.setPassword("1234567");
user.setEmail("[email protected]");
user.setIdCard("123456190001011234");
user.setPhone("18888888888");
user.setIdNo("130701199310308888");
return user;
}
- 测试代码
final String originalStr = "UserIdNo{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888', idNo='130701199310308888'}";
final String sensitiveStr = "UserIdNo{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888', idNo='130*************88'}";
final String expectSensitiveJson = "{\"email\":\"123**@qq.com\",\"idCard\":\"123456**********34\",\"idNo\":\"130*************88\",\"phone\":\"188****8888\",\"username\":\"脱*君\"}";
UserIdNo user = DataPrepareTest.buildUserIdNo();
UserIdNo sensitiveUser = SensitiveUtil.desCopy(user);
Assert.assertEquals(sensitiveStr, sensitiveUser.toString());
Assert.assertEquals(originalStr, user.toString());
String sensitiveJson = SensitiveUtil.desJson(user);
Assert.assertEquals(expectSensitiveJson, sensitiveJson);
我们可以直接利用 sensitiveUser
去打印日志信息,而这个对象对于代码其他流程不影响,我们依然可以使用原来的 user
对象。
当然,也可以使用 sensitiveJson
打印日志信息。
默认情况下,我们指定的场景都是生效的。
但是你可能需要有些情况下不进行脱敏,比如有些用户密码为 123456,你觉得这种用户不脱敏也罢。
- UserPasswordCondition.java
@Sensitive(condition = ConditionFooPassword.class, strategy = StrategyPassword.class)
private String password;
其他保持不变,我们指定了一个 condition,实现如下:
- ConditionFooPassword.java
public class ConditionFooPassword implements ICondition {
@Override
public boolean valid(IContext context) {
try {
Field field = context.getCurrentField();
final Object currentObj = context.getCurrentObject();
final String password = (String) field.get(currentObj);
return !password.equals("123456");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
也就是只有当密码不是 123456 时密码脱敏策略才会生效。
上面的例子是基于注解式的编程,如果你只是单个字段。比如
- singleSensitiveTest
@Test
public void singleSensitiveTest() {
final String email = "[email protected]";
IStrategy strategy = new StrategyEmail();
final String emailSensitive = (String) strategy.des(email, null);
System.out.println("脱敏后的邮箱:" + emailSensitive);
}
- 日志信息
脱敏后的邮箱:123***@qq.com
如果某个属性是单个集合或者对象,则需要使用注解 @SensitiveEntry
。
- 放在集合属性上,且属性为普通对象
会遍历每一个属性,执行上面的脱敏策略。
- 放在对象属性上
会处理对象中各个字段上的脱敏注解信息。
- 放在集合属性上,且属性为对象
遍历每一个对象,处理对象中各个字段上的脱敏注解信息。
- UserEntryBaseType.java
作为演示,集合中为普通的字符串。
public class UserEntryBaseType {
@SensitiveEntry
@Sensitive(strategy = StrategyChineseName.class)
private List<String> chineseNameList;
@SensitiveEntry
@Sensitive(strategy = StrategyChineseName.class)
private String[] chineseNameArray;
//Getter & Setter & toString()
}
- 构建对象
/**
* 构建用户-属性为列表,列表中为基础属性
* @return 构建嵌套信息
* @since 0.0.2
*/
public static UserEntryBaseType buildUserEntryBaseType() {
UserEntryBaseType userEntryBaseType = new UserEntryBaseType();
userEntryBaseType.setChineseNameList(Arrays.asList("盘古", "女娲", "伏羲"));
userEntryBaseType.setChineseNameArray(new String[]{"盘古", "女娲", "伏羲"});
return userEntryBaseType;
}
- 测试演示
/**
* 用户属性中有集合或者map,集合中属性是基础类型-脱敏测试
* @since 0.0.2
*/
@Test
public void sensitiveEntryBaseTypeTest() {
UserEntryBaseType userEntryBaseType = DataPrepareTest.buildUserEntryBaseType();
System.out.println("脱敏前原始: " + userEntryBaseType);
UserEntryBaseType sensitive = SensitiveUtil.desCopy(userEntryBaseType);
System.out.println("脱敏对象: " + sensitive);
System.out.println("脱敏后原始: " + userEntryBaseType);
}
- 日志信息
脱敏前原始: UserEntryBaseType{chineseNameList=[盘古, 女娲, 伏羲], chineseNameArray=[盘古, 女娲, 伏羲]}
脱敏对象: UserEntryBaseType{chineseNameList=[*古, *娲, *羲], chineseNameArray=[*古, *娲, *羲]}
脱敏后原始: UserEntryBaseType{chineseNameList=[盘古, 女娲, 伏羲], chineseNameArray=[盘古, 女娲, 伏羲]}
- 演示对象
这里的 User 和上面的 User 对象一致。
public class UserEntryObject {
@SensitiveEntry
private User user;
@SensitiveEntry
private List<User> userList;
@SensitiveEntry
private User[] userArray;
//...
}
- 对象构建
/**
* 构建用户-属性为列表,数组。列表中为对象。
* @return 构建嵌套信息
* @since 0.0.2
*/
public static UserEntryObject buildUserEntryObject() {
UserEntryObject userEntryObject = new UserEntryObject();
User user = buildUser();
User user2 = buildUser();
User user3 = buildUser();
userEntryObject.setUser(user);
userEntryObject.setUserList(Arrays.asList(user2));
userEntryObject.setUserArray(new User[]{user3});
return userEntryObject;
}
- 测试演示
/**
* 用户属性中有集合或者对象,集合中属性是对象-脱敏测试
* @since 0.0.2
*/
@Test
public void sensitiveEntryObjectTest() {
UserEntryObject userEntryObject = DataPrepareTest.buildUserEntryObject();
System.out.println("脱敏前原始: " + userEntryObject);
UserEntryObject sensitiveUserEntryObject = SensitiveUtil.desCopy(userEntryObject);
System.out.println("脱敏对象: " + sensitiveUserEntryObject);
System.out.println("脱敏后原始: " + userEntryObject);
}
- 测试结果
脱敏前原始: UserEntryObject{user=User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}, userList=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userArray=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}]}
脱敏对象: UserEntryObject{user=User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}, userList=[User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}], userArray=[User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}]}
脱敏后原始: UserEntryObject{user=User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}, userList=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userArray=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}]}
如果你看了前面的内容,会看到这样的代码:
@Sensitive(strategy = StrategyChineseName.class)
private String username;
但是这种需求很常见,就引入更简单的写法,如下:
@SensitiveStrategyChineseName
private String name;
和上面效果是一致的。
不足:无法灵活指定生效条件,下个版本准备解决这个问题。
注解 | 等价 @Sensitive | 备注 |
---|---|---|
@SensitiveStrategyChineseName |
@Sensitive(strategy = StrategyChineseName.class) |
中文名称脱敏 |
@SensitiveStrategyPassword |
@Sensitive(strategy = StrategyPassword.class) |
密码脱敏 |
@SensitiveStrategyEmail |
@Sensitive(strategy = StrategyEmail.class) |
email 脱敏 |
@SensitiveStrategyCardId |
@Sensitive(strategy = StrategyCardId.class) |
卡号脱敏 |
@SensitiveStrategyPhone |
@Sensitive(strategy = StrategyPhone.class) |
手机号脱敏 |
@SensitiveStrategyIdNo |
@Sensitive(strategy = StrategyIdNo.class) |
身份证脱敏 |
使用的方式和 @Sensitive
是一样的,只是一种简化,方便日常使用。
与 @SensitiveEntry
的结合和 @Sensitive
完全一致,此处不再演示。
- SystemBuiltInAtIdNo.java
定义测试对象
@SensitiveStrategyPhone
private String phone;
@SensitiveStrategyPassword
private String password;
@SensitiveStrategyChineseName
private String name;
@SensitiveStrategyEmail
private String email;
@SensitiveStrategyCardId
private String cardId;
@SensitiveStrategyIdNo
private String idNo;
- 对象构建
public static SystemBuiltInAtIdNo buildSystemBuiltInAtIdNo() {
SystemBuiltInAtIdNo dto = new SystemBuiltInAtIdNo();
dto.setName("脱敏君");
dto.setPassword("1234567");
dto.setEmail("[email protected]");
dto.setCardId("123456190001011234");
dto.setPhone("18888888888");
dto.setIdNo("130701199310308888");
return dto;
}
- 测试方法
测试方法断言如下。
final String expectOriginalStr = "SystemBuiltInAtIdNo{phone='18888888888', password='1234567', name='脱敏君', email='[email protected]', cardId='123456190001011234', idNo='130701199310308888'}";
final String expectSensitiveStr = "SystemBuiltInAtIdNo{phone='188****8888', password='null', name='脱*君', email='123**@qq.com', cardId='123456**********34', idNo='130*************88'}";
final String expectSensitiveJson = "{\"cardId\":\"123456**********34\",\"email\":\"123**@qq.com\",\"idNo\":\"130*************88\",\"name\":\"脱*君\",\"phone\":\"188****8888\"}";
SystemBuiltInAtIdNo original = DataPrepareTest.buildSystemBuiltInAtIdNo();
SystemBuiltInAtIdNo sensitive = SensitiveUtil.desCopy(original);
Assert.assertEquals(expectOriginalStr, original.toString());
Assert.assertEquals(expectSensitiveStr, sensitive.toString());
String sensitiveJson = SensitiveUtil.desJson(original);
Assert.assertEquals(expectSensitiveJson, sensitiveJson);
和上面使用方式类似。original
的属性不受任何影响,可以使用 sensitive
或者 sensitiveJson
输出日志。
如果你将新增的注解 @SensitiveStrategyChineseName
与 @Sensitive
同时在一个字段上使用。
为了简化逻辑,优先选择执行 @Sensitive
,如果 @Sensitive
执行脱敏,
那么 @SensitiveStrategyChineseName
将不会生效。
如:
/**
* 测试字段
* 1.当多种注解混合的时候,为了简化逻辑,优先选择 @Sensitive 注解。
*/
@SensitiveStrategyChineseName
@Sensitive(strategy = StrategyPassword.class)
private String testField;
- v0.0.4 新增功能。允许功能自定义条件注解和策略注解。
- v0.0.11 新增功能。允许功能自定义级联脱敏注解。
- 策略脱敏
/**
* 自定义密码脱敏策略
* @author binbin.hou
* date 2019/1/17
* @since 0.0.4
*/
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveStrategy(CustomPasswordStrategy.class)
public @interface SensitiveCustomPasswordStrategy {
}
- 脱敏生效条件
/**
* 自定义密码脱敏策略生效条件
* @author binbin.hou
* date 2019/1/17
* @since 0.0.4
*/
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveCondition(ConditionFooPassword.class)
public @interface SensitiveCustomPasswordCondition{
}
- TIPS
@SensitiveStrategy
策略单独使用的时候,默认是生效的。
如果有 @SensitiveCondition
注解,则只有当条件满足时,才会执行脱敏策略。
@SensitiveCondition
只会对系统内置注解和自定义注解生效,因为 @Sensitive
有属于自己的策略生效条件。
- 策略优先级
@Sensitive
优先生效,然后是系统内置注解,最后是用户自定义注解。
两个元注解 @SensitiveStrategy
、@SensitiveCondition
分别指定了对应的实现。
- CustomPasswordStrategy.java
public class CustomPasswordStrategy implements IStrategy {
@Override
public Object des(Object original, IContext context) {
return "**********************";
}
}
- ConditionFooPassword.java
/**
* 让这些 123456 的密码不进行脱敏
* @author binbin.hou
* date 2019/1/2
* @since 0.0.1
*/
public class ConditionFooPassword implements ICondition {
@Override
public boolean valid(IContext context) {
try {
Field field = context.getCurrentField();
final Object currentObj = context.getCurrentObject();
final String name = (String) field.get(currentObj);
return !name.equals("123456");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
定义一个使用自定义注解的对象。
public class CustomPasswordModel {
@SensitiveCustomPasswordCondition
@SensitiveCustomPasswordStrategy
private String password;
@SensitiveCustomPasswordCondition
@SensitiveStrategyPassword
private String fooPassword;
//其他方法
}
/**
* 自定义注解测试
*/
@Test
public void customAnnotationTest() {
final String originalStr = "CustomPasswordModel{password='hello', fooPassword='123456'}";
final String sensitiveStr = "CustomPasswordModel{password='**********************', fooPassword='123456'}";
CustomPasswordModel model = buildCustomPasswordModel();
Assert.assertEquals(originalStr, model.toString());
CustomPasswordModel sensitive = SensitiveUtil.desCopy(model);
Assert.assertEquals(sensitiveStr, sensitive.toString());
Assert.assertEquals(originalStr, model.toString());
}
构建对象的方法如下:
/**
* 构建自定义密码对象
* @return 对象
*/
private CustomPasswordModel buildCustomPasswordModel(){
CustomPasswordModel model = new CustomPasswordModel();
model.setPassword("hello");
model.setFooPassword("123456");
return model;
}
- v0.0.11 新增功能。允许功能自定义级联脱敏注解。
- 自定义级联脱敏注解
可以根据自己的业务需要,在自定义的注解上使用 @SensitiveEntry
。
使用方式保持和 @SensitiveEntry
一样即可。
/**
* 级联脱敏注解,如果对象中属性为另外一个对象(集合),则可以使用这个注解指定。
* <p>
* 1. 如果属性为 Iterable 的子类集合,则当做列表处理,遍历其中的对象
* 2. 如果是普通对象,则处理对象中的脱敏信息
* 3. 如果是普通字段/MAP,则不做处理
*
* @author dev-sxl
* date 2020-09-14
* @since 0.0.11
*/
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@SensitiveEntry
public @interface SensitiveEntryCustom {
}
定义一个使用自定义注解的对象。
public class CustomUserEntryObject {
@SensitiveEntryCustom
private User user;
@SensitiveEntryCustom
private List<User> userList;
@SensitiveEntryCustom
private User[] userArray;
// 其他方法...
}
/**
* 用户属性中有集合或者对象,集合中属性是对象-脱敏测试
* @since 0.0.11
*/
@Test
public void customSensitiveEntryObjectTest() {
final String originalStr = "CustomUserEntryObject{user=User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}, userList=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userArray=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}]}";
final String sensitiveStr = "CustomUserEntryObject{user=User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}, userList=[User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}], userArray=[User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}]}";
CustomUserEntryObject userEntryObject = DataPrepareTest.buildCustomUserEntryObject();
Assert.assertEquals(originalStr, userEntryObject.toString());
CustomUserEntryObject sensitiveUserEntryObject = SensitiveUtil.desCopy(userEntryObject);
Assert.assertEquals(sensitiveStr, sensitiveUserEntryObject.toString());
Assert.assertEquals(originalStr, userEntryObject.toString());
}
构建对象的方法如下:
/**
* 构建用户-属性为列表,数组。列表中为对象。
*
* @return 构建嵌套信息
* @since 0.0.11
*/
public static CustomUserEntryObject buildCustomUserEntryObject() {
CustomUserEntryObject userEntryObject = new CustomUserEntryObject();
User user = buildUser();
User user2 = buildUser();
User user3 = buildUser();
userEntryObject.setUser(user);
userEntryObject.setUserList(Arrays.asList(user2));
userEntryObject.setUserArray(new User[]{user3});
return userEntryObject;
}
/**
* 构建测试用户对象
*
* @return 创建后的对象
* @since 0.0.1
*/
public static User buildUser() {
User user = new User();
user.setUsername("脱敏君");
user.setPassword("1234567");
user.setEmail("[email protected]");
user.setIdCard("123456190001011234");
user.setPhone("18888888888");
return user;
}
为了避免生成中间脱敏对象,v0.0.6 之后直接支持生成脱敏后的 JSON。
新增工具类方法,可以直接返回脱敏后的 JSON。
生成的 JSON 是脱敏的,原对象属性值不受影响。
public static String desJson(Object object)
和 SensitiveUtil.desCopy()
完全一致。
所有的测试案例中,都添加了对应的 desJson(Object)
测试代码,可以参考。
此处只展示最基本的使用。
@Test
public void sensitiveJsonTest() {
final String originalStr = "SystemBuiltInAt{phone='18888888888', password='1234567', name='脱敏君', email='[email protected]', cardId='123456190001011234'}";
final String sensitiveJson = "{\"cardId\":\"123456**********34\",\"email\":\"123**@qq.com\",\"name\":\"脱*君\",\"phone\":\"188****8888\"}";
SystemBuiltInAt systemBuiltInAt = DataPrepareTest.buildSystemBuiltInAt();
Assert.assertEquals(sensitiveJson, SensitiveUtil.desJson(systemBuiltInAt));
Assert.assertEquals(originalStr, systemBuiltInAt.toString());
}
本次 JSON 脱敏基于 FastJSON。
FastJSON 在序列化本身存在一定限制。当对象中有集合,集合中还是对象时,结果不尽如人意。
本测试案例可见测试代码。
@Test
public void sensitiveUserCollectionJsonTest() {
final String originalStr = "UserCollection{userList=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userSet=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userCollection=[User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}], userMap={map=User{username='脱敏君', idCard='123456190001011234', password='1234567', email='[email protected]', phone='18888888888'}}}";
final String commonJson = "{\"userArray\":[{\"email\":\"[email protected]\",\"idCard\":\"123456190001011234\",\"password\":\"1234567\",\"phone\":\"18888888888\",\"username\":\"脱敏君\"}],\"userCollection\":[{\"$ref\":\"$.userArray[0]\"}],\"userList\":[{\"$ref\":\"$.userArray[0]\"}],\"userMap\":{\"map\":{\"$ref\":\"$.userArray[0]\"}},\"userSet\":[{\"$ref\":\"$.userArray[0]\"}]}";
final String sensitiveJson = "{\"userArray\":[{\"email\":\"123**@qq.com\",\"idCard\":\"123456**********34\",\"phone\":\"188****8888\",\"username\":\"脱*君\"}],\"userCollection\":[{\"$ref\":\"$.userArray[0]\"}],\"userList\":[{\"$ref\":\"$.userArray[0]\"}],\"userMap\":{\"map\":{\"$ref\":\"$.userArray[0]\"}},\"userSet\":[{\"$ref\":\"$.userArray[0]\"}]}";
UserCollection userCollection = DataPrepareTest.buildUserCollection();
Assert.assertEquals(commonJson, JSON.toJSONString(userCollection));
Assert.assertEquals(sensitiveJson, SensitiveUtil.desJson(userCollection));
Assert.assertEquals(originalStr, userCollection.toString());
}
如果有这种需求,建议使用原来的 desCopy(Object)
。
v0.0.7
支持的新特性,便于用户处理集合相关的脱敏。
如果列表为空,则直接返回空列表。
更多测试代码参见 SensitiveUtilCollectionTest.java
- List desCopyCollection(Collection collection)
返回脱敏后的对象集合
List<User> userList = DataPrepareTest.buildUserList();
List<User> sensitiveList = SensitiveUtil.desCopyCollection(userList);
Assert.assertEquals("[User{username='脱*君', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}, User{username='集**试', idCard='123456**********34', password='null', email='123**@qq.com', phone='188****8888'}]", sensitiveList.toString());
- List desJsonCollection(Collection<?> collection)
返回脱敏后的 json 列表
List<User> userList = DataPrepareTest.buildUserList();
List<String> sensitiveJsonList = SensitiveUtil.desJsonCollection(userList);
Assert.assertEquals("[{\"email\":\"123**@qq.com\",\"idCard\":\"123456**********34\",\"phone\":\"188****8888\",\"username\":\"脱*君\"}, {\"email\":\"123**@qq.com\",\"idCard\":\"123456**********34\",\"phone\":\"188****8888\",\"username\":\"集**试\"}]", sensitiveJsonList.toString());
为了配置的灵活性,引入了引导类。
SensitiveBs
引导类的核心方法列表如下:
序号 | 方法 | 参数 | 结果 | 说明 |
---|---|---|---|---|
1 | desCopy() | 目标对象 | 深度拷贝脱敏对象 | 适应性更强 |
2 | desJson() | 目标对象 | 脱敏对象 json | 性能较好 |
使用方式和工具类一致,示意如下:
SensitiveBs.newInstance().desCopy(user);
默认的使用 FastJson 进行对象的深度拷贝,等价于:
SensitiveBs.newInstance()
.deepCopy(FastJsonDeepCopy.getInstance())
.desJson(user);
deepCopy 用于指定深度复制的具体实现,支持用户自定义。
深度复制可以保证我们日志输出对象脱敏,同时不影响正常业务代码的使用。
可以实现深度复制的方式有很多种,默认基于 fastjson 实现的。
为保证后续良性发展,v0.0.13 版本之后将深度复制接口抽离为单独的项目:
目前支持 6 种基于序列化实现的深度复制,便于用户替换使用。
每一种都可以单独使用,保证依赖更加轻量。
为满足不同场景的需求,深度复制策略支持用户自定义。
如果你对本项目有兴趣,并且对代码有一定追求,可以申请加入本项目开发。
如果你善于写文档,或者愿意补全测试案例,也非常欢迎加入。
-
针对身份证的默认脱敏策略
-
脱敏实现独立为工具方法,便于直接使用。
喜欢重载 toString(),或特殊的场景
- 考虑添加针对 MAP 的脱敏支持