java 日志脱敏
文章目录
前言
在开发中,我们经常会处理一些敏感数据,比如:身份证号、手机号、银行卡、邮箱等等。为了保护用户的数据安全,我们需要对这些数据进行脱敏处理。JAVA提供了一些工具,可以让我们更加便捷的去处理。
方式一、Sensitive注解
Sensitive 是一个基于注解的脱敏工具,可以通过在实体类的字段上添加注解来指定脱敏规则。以下是一个示例:
public class User {
@Sensitive(type = SensitiveType.CHINESE_NAME)
private String name;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
// 省略其他字段及 getter/setter 方法
}
方式二、Apache Commons Text
Apache Commons Text是Apache的一个开源项目,提供了一些常用的文本处理工具。它包含了一个用于脱敏处理的工具类:org.apache.commons.text.StringEscapeUtils,可以方便地对敏感信息进行字符替换、字符隐藏等操作
方式三、MaskTools
MaskTools 是一个基于正则表达式的脱敏工具,可以通过指定正则表达式来匹配敏感数据,并对其进行脱敏处理。以下是一个示例:
String regex = "(\\d{3})\\d{4}(\\d{4})";
String replacement = "$1****$2";
String idCard = "1234567890123456";
String maskedIdCard = MaskTools.mask(idCard, regex, replacement);
System.out.println(maskedIdCard);
在上面的示例中,正则表达式 (\d{3})\d{4}(\d{4}) 匹配身份证号的中间部分,替换为 $1****$2,即保留前四位和后四位,中间部分用星号代替。
方式四、Logback
Logback Sensitive是一个基于Logback的数据脱敏插件,它可以在日志输出时自动对敏感信息进行脱敏处理。Logback Sensitive支持多种脱敏方法,并且可以通过配置文件进行灵活的配置
方式五、hutool工具包
3.1 引入Maven配置
在项目的pom.xml的dependencies中加入以下内容,这里以5.8.16版本为例。
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
3.2 Hutool包含的组件
一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类,同时提供以下组件:
模块 | 介绍 |
---|---|
hutool-aop | DK动态代理封装,提供非IOC下的切面支持 |
hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
hutool-cache | 简单缓存实现 |
hutool-core | 核心,包括Bean操作、日期、各种Util等 |
hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
hutool-extra | 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
hutool-http | 基于HttpUrlConnection的Http客户端封装 |
hutool-log | 自动识别日志实现的日志门面 |
hutool-json | JSON实现 |
hutool-poi | 针对POI中Excel和Word的封装 |
hutool-captcha | 图片验证码实现 |
可以根据需求对每个模块单独引入,也可以通过引入hutool-all方式引入所有模块,本文所使用的数据脱敏工具就是在hutool.core模块。
3.3 Hutool支持的脱敏示例
现阶段最新版本的Hutool支持的脱敏数据类型如下,基本覆盖了常见的敏感信息。
用户id
中文姓名
身份证号
座机号
手机号
地址
电子邮件
密码
中国大陆车牌,包含普通车辆、新能源车辆
银行卡
import cn.hutool.core.util.DesensitizedUtil;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
*
* @description: Hutool实现数据脱敏
*/
@SpringBootTest
public class HuToolDesensitizationTest {
@Test
public void testPhoneDesensitization(){
String phone="13723231234";
System.out.println(DesensitizedUtil.mobilePhone(phone)); //输出:137****1234
}
@Test
public void testBankCardDesensitization(){
String bankCard="6217000130008255666";
System.out.println(DesensitizedUtil.bankCard(bankCard)); //输出:6217 **** **** *** 5666
}
@Test
public void testIdCardNumDesensitization(){
String idCardNum="411021199901102321";
//只显示前4位和后2位
System.out.println(DesensitizedUtil.idCardNum(idCardNum,4,2)); //输出:4110************21
}
@Test
public void testPasswordDesensitization(){
String password="www.jd.com_35711";
System.out.println(DesensitizedUtil.password(password)); //输出:****************
}
}
3.4 通过注解方式实现脱敏
如果前端需要显示数据数据的地方比较多,我们不可能在每个地方都调用一个工具类,这样就显得代码太冗余了,那我们如何通过注解的方式优雅的完成数据脱敏呢?
3.4.1 脱敏策略的枚举
/**
* @author
* @description:脱敏策略枚举
*/
public enum DesensitizationTypeEnum {
//自定义
MY_RULE,
//用户id
USER_ID,
//中文名
CHINESE_NAME,
//身份证号
ID_CARD,
//座机号
FIXED_PHONE,
//手机号
MOBILE_PHONE,
//地址
ADDRESS,
//电子邮件
EMAIL,
//密码
PASSWORD,
//中国大陆车牌,包含普通车辆、新能源车辆
CAR_LICENSE,
//银行卡
BANK_CARD
}
3.4.2 定义Desensitization 注解
@Retention(RetentionPolicy.RUNTIME):运行时生效。
@Target(ElementType.FIELD):可用在字段上。
@JacksonAnnotationsInside:此注解可以点进去看一下是一个元注解,主要是用户打包其他注解一起使用。
@JsonSerialize:上面说到过,该注解的作用就是可自定义序列化,可以用在注解上,方法上,字段上,类上,运行时生效等等,根据提供的序列化类里面的重写方法实现自定义序列化。
/**
* @author
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface Desensitization {
/**
* 脱敏数据类型,在MY_RULE的时候,startInclude和endExclude生效
*/
DesensitizationTypeEnum type() default DesensitizationTypeEnum.MY_RULE;
/**
* 脱敏开始位置(包含)
*/
int startInclude() default 0;
/**
* 脱敏结束位置(不包含)
*/
int endExclude() default 0;
}
注:只有使用了自定义的脱敏枚举MY_RULE的时候,开始位置和结束位置才生效。
3.4.3 创建自定的序列化类
这一步是我们实现数据脱敏的关键。自定义序列化类继承 JsonSerializer,实现ContextualSerializer接口,并重写两个方法。
/**
* @author
* @description: 自定义序列化类
*/
@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;
// userId脱敏
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) {
// 判断数据类型是否为String类型
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
// 获取定义的注解
Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
// 为null
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
// 不为null
if (desensitization != null) {
// 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
return new DesensitizationSerialize(desensitization.type(), desensitization.startInclude(),
desensitization.endExclude());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
3.4.4 示例
/**
*
* @description:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestPojo {
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;
}
@RestController
public class TestController {
@RequestMapping("/test")
public TestPojo testDesensitization(){
TestPojo testPojo = new TestPojo();
testPojo.setUserName("我是用户名");
testPojo.setAddress("地球中国-北京市通州区京东总部2号楼");
testPojo.setPhone("13782946666");
testPojo.setPassword("sunyangwei123123123.");
System.out.println(testPojo);
return testPojo;
}
}
结果:
方式六、 FastJson实现数据脱敏
https://juejin.cn/post/7067916686141161479)https://juejin.cn/post/7067916686141161479