数据脱敏
数据脱敏,指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护
。
需要进行数据脱敏的地方:如身份证号、手机号、卡号、客户号等个人信息
此处通过利用 springboot 自带的
jackson 自定义序列化
实现。它的实现原理其实就是在json 进行序列化渲染给前端时,进行脱敏
1 2 3 4 5
| <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.0</version> </dependency>
|
现阶段版本Hutool支持的脱敏数据类型
如下,基本覆盖了常见的敏感信息。
具体如:用户id
、中文姓名、身份证号、座机号、手机号、地址、电子邮件、密码、中国大陆车牌,包含普通车辆、新能源车辆、银行卡
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
@SpringBootTest public class HuToolDesensitizationTest {
@Test public void testPhoneDesensitization(){ String phone="13723231234"; System.out.println(DesensitizedUtil.mobilePhone(phone)); } @Test public void testBankCardDesensitization(){ String bankCard="6217000130008255666"; System.out.println(DesensitizedUtil.bankCard(bankCard)); }
@Test public void testIdCardNumDesensitization(){ String idCardNum="411021199901102321"; System.out.println(DesensitizedUtil.idCardNum(idCardNum,4,2)); } @Test public void testPasswordDesensitization(){ String password="www.jd.com_35711"; System.out.println(DesensitizedUtil.password(password)); }
}
|
脱敏枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public enum DesensitizationTypeEnum { MY_RULE, USER_ID, CHINESE_NAME, ID_CARD, FIXED_PHONE, MOBILE_PHONE, ADDRESS, EMAIL, PASSWORD, CAR_LICENSE, BANK_CARD }
|
脱敏注解
@Retention(RetentionPolicy.RUNTIME):运行时生效。
@Target(ElementType.FIELD):可用在字段上。
@JacksonAnnotationsInside:此注解可以点进去看一下是一个元注解,主要是用户打包其他注解一起使用。
@JsonSerialize:上面说到过,该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = DesensitizationSerialize.class) public @interface Desensitization {
DesensitizationTypeEnum type() default DesensitizationTypeEnum.MY_RULE;
int startInclude() default 0;
int endExclude() default 0;
}
|
自定义序列化类
这一步是我们实现数据脱敏的关键。自定义序列化类继承
JsonSerializer,实现ContextualSerializer接口,并重写两个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
|
@AllArgsConstructor @NoArgsConstructor public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer { private DesensitizationTypeEnum type;
private Integer startInclude;
private Integer endExclude;
@Override public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { switch (type) { case MY_RULE: jsonGenerator.writeString(CharSequenceUtil.hide(str, startInclude, endExclude)); break; case USER_ID: jsonGenerator.writeString(String.valueOf(DesensitizedUtil.userId())); break; case CHINESE_NAME: jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str))); break; case ID_CARD: jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 1, 2)); break; case FIXED_PHONE: jsonGenerator.writeString(DesensitizedUtil.fixedPhone(String.valueOf(str))); break; case MOBILE_PHONE: jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str))); break; case ADDRESS: jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8)); break; case EMAIL: jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str))); break; case PASSWORD: jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str))); break; case CAR_LICENSE: jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str))); break; case BANK_CARD: jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str))); break; default: }
}
@Override public 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 DesensitizationSerialize(desensitization.type(), desensitization.startInclude(), desensitization.endExclude()); } }
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty); } return serializerProvider.findNullValueSerializer(null); } }
|
测试
接口
1 2 3 4 5 6
| @GetMapping("desensitization") @ApiOperation(value = "数据脱敏") @PreAuthorize("hasAnyAuthority('system:test:list')") public R<TestVo> desensitization(){ return debugManager.desensitization(); }
|
业务层
1 2 3 4 5 6 7 8
| public R<TestVo> desensitization() { TestVo testVo = new TestVo(); testVo.setUserName("我是用户名"); testVo.setAddress("地球中国-北京市通州区京东总部2号楼"); testVo.setPhone("13782946666"); testVo.setPassword("sunyangwei123123123."); return success(testVo); }
|
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Data @NoArgsConstructor @AllArgsConstructor public class TestVo {
private String userName;
@Desensitization(type = DesensitizationTypeEnum.MOBILE_PHONE) private String phone;
@Desensitization(type = DesensitizationTypeEnum.PASSWORD) private String password;
@Desensitization(type = DesensitizationTypeEnum.MY_RULE, startInclude = 0, endExclude = 2) private String address;
}
|
响应
1 2 3 4 5 6 7 8 9 10
| { "code": 0, "message": "", "data": { "userName": "我是用户名", "phone": "137****6666", "password": "********************", "address": "**中国-北京市通州区京东总部2号楼" } }
|