Fastjson实现数据脱敏
步骤
1. 创建数据脱敏注解
注解标记到类的属性上,并且指定脱敏策略。
@Documented
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Desensitive {
/**
* 数据脱敏策略(类型),默认无
*/
DesensitizedType strategy() default DesensitizedType.NONE;
}
2. 创建数据脱敏规则枚举类
搭配自定义注解使用。
/**
* 自定义脱敏数据类型 枚举类
*/
public enum DesensitizedType {
/**
* 默认无
*/
NONE,
/**
* 用户id
*/
USER_ID,
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 座机号
*/
FIXED_PHONE,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 电子邮件
*/
EMAIL,
/**
* 密码
*/
PASSWORD,
/**
* 中国大陆车牌,包含普通车辆、新能源车辆
*/
CAR_LICENSE,
/**
* 银行卡
*/
BANK_CARD
}
3. 工具类——枚举类的类型对应的脱敏逻辑
实现方法来自Hutool工具类中DesensitizedUtil
的方法
public class DesensitizedUtil {
/**
* 【用户id】不对外提供userId
*
* @return 脱敏后的主键
*/
public static Long userId() {
return 0L;
}
/**
* 【中文姓名】只显示第一个汉字,其他隐藏为2个星号,比如:李**
*
* @param fullName 姓名
* @return 脱敏后的姓名
*/
public static String chineseName(String fullName) {
if (StrUtil.isBlank(fullName)) {
return StrUtil.EMPTY;
}
return StrUtil.hide(fullName, 1, fullName.length());
}
/**
* 【身份证号】前1位 和后2位
*
* @param idCardNum 身份证
* @param front 保留:前面的front位数;从1开始
* @param end 保留:后面的end位数;从1开始
* @return 脱敏后的身份证
*/
public static String idCardNum(String idCardNum, int front, int end) {
//身份证不能为空
if (StrUtil.isBlank(idCardNum)) {
return StrUtil.EMPTY;
}
//需要截取的长度不能大于身份证号长度
if ((front + end) > idCardNum.length()) {
return StrUtil.EMPTY;
}
//需要截取的不能小于0
if (front < 0 || end < 0) {
return StrUtil.EMPTY;
}
return StrUtil.hide(idCardNum, front, idCardNum.length() - end);
}
/**
* 【固定电话 前四位,后两位
*
* @param num 固定电话
* @return 脱敏后的固定电话;
*/
public static String fixedPhone(String num) {
if (StrUtil.isBlank(num)) {
return StrUtil.EMPTY;
}
return StrUtil.hide(num, 4, num.length() - 2);
}
/**
* 【手机号码】前三位,后4位,其他隐藏,比如135****2210
*
* @param num 移动电话;
* @return 脱敏后的移动电话;
*/
public static String mobilePhone(String num) {
if (StrUtil.isBlank(num)) {
return StrUtil.EMPTY;
}
return StrUtil.hide(num, 3, num.length() - 4);
}
/**
* 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
*
* @param address 家庭住址
* @param sensitiveSize 敏感信息长度
* @return 脱敏后的家庭地址
*/
public static String address(String address, int sensitiveSize) {
if (StrUtil.isBlank(address)) {
return StrUtil.EMPTY;
}
int length = address.length();
return StrUtil.hide(address, length - sensitiveSize, length);
}
/**
* 【电子邮箱】邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
*
* @param email 邮箱
* @return 脱敏后的邮箱
*/
public static String email(String email) {
if (StrUtil.isBlank(email)) {
return StrUtil.EMPTY;
}
int index = StrUtil.indexOf(email, '@');
if (index <= 1) {
return email;
}
return StrUtil.hide(email, 1, index);
}
/**
* 【密码】密码的全部字符都用*代替,比如:******
*
* @param password 密码
* @return 脱敏后的密码
*/
public static String password(String password) {
if (StrUtil.isBlank(password)) {
return StrUtil.EMPTY;
}
return StrUtil.repeat('*', password.length());
}
/**
* 【中国车牌】车牌中间用*代替
* eg1:null -》 ""
* eg1:"" -》 ""
* eg3:苏D40000 -》 苏D4***0
* eg4:陕A12345D -》 陕A1****D
* eg5:京A123 -》 京A123 如果是错误的车牌,不处理
*
* @param carLicense 完整的车牌号
* @return 脱敏后的车牌
*/
public static String carLicense(String carLicense) {
if (StrUtil.isBlank(carLicense)) {
return StrUtil.EMPTY;
}
// 普通车牌
if (carLicense.length() == 7) {
carLicense = StrUtil.hide(carLicense, 3, 6);
} else if (carLicense.length() == 8) {
// 新能源车牌
carLicense = StrUtil.hide(carLicense, 3, 7);
}
return carLicense;
}
/**
* 银行卡号脱敏
* eg: 1101 **** **** **** 3256
*
* @param bankCardNo 银行卡号
* @return 脱敏之后的银行卡号
* @since 5.6.3
*/
public static String bankCard(String bankCardNo) {
if (StrUtil.isBlank(bankCardNo)) {
return bankCardNo;
}
bankCardNo = StrUtil.trim(bankCardNo);
if (bankCardNo.length() < 9) {
return bankCardNo;
}
final int length = bankCardNo.length();
final int midLength = length - 8;
final StringBuilder buf = new StringBuilder();
buf.append(bankCardNo, 0, 4);
for (int i = 0; i < midLength; ++i) {
if (i % 4 == 0) {
buf.append(CharUtil.SPACE);
}
buf.append('*');
}
buf.append(CharUtil.SPACE).append(bankCardNo, length - 4, length);
return buf.toString();
}
}
4. 创建fastjson数据过滤器,结合注解和脱敏规则对数据进行处理
/**
* 脱敏数据转换过滤器
*/
public class DesensitizeValueFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
try {
Field field = object.getClass().getDeclaredField(name);
Desensitive desensitive = field.getAnnotation(Desensitive.class);
if (desensitive == null) {
return value;
}
if (value instanceof Long) {
return String.valueOf(DesensitizedUtil.userId());
}
if (!(value instanceof String valueStr) || ((String) value).length() == 0) {
return value;
}
DesensitizedType strategy = desensitive.strategy();
switch (strategy) {
case CHINESE_NAME:
return DesensitizedUtil.chineseName(valueStr);
case ID_CARD:
return DesensitizedUtil.idCardNum(valueStr, 1, 2);
case FIXED_PHONE:
return DesensitizedUtil.fixedPhone(valueStr);
case MOBILE_PHONE:
return DesensitizedUtil.mobilePhone(valueStr);
case ADDRESS:
return DesensitizedUtil.address(valueStr, 8);
case EMAIL:
return DesensitizedUtil.email(valueStr);
case PASSWORD:
return DesensitizedUtil.password(valueStr);
case CAR_LICENSE:
return DesensitizedUtil.carLicense(valueStr);
case BANK_CARD:
return DesensitizedUtil.bankCard(valueStr);
case NONE:
return value;
default:
}
} catch (NoSuchFieldException e) {
return value;
}
return value;
}
}
5. 在配置类中创建fastjson转换器bean,并将上述的过滤器添加进来
@Configuration
public class FastJsonConverterBeanConfig {
@Bean
public FastJsonHttpMessageConverter fastJsonHttpMessageConverters() {
// 1.定义一个converters转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
// 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// 添加自己写的拦截器
fastJsonConfig.setSerializeFilters(new DesensitizeValueFilter());
// 3.在converter中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
// 4.返回HttpMessageConverters对象
return fastConverter;
}
}
6. 敏感数据实体对象
/**
* 用户敏感数据-自定义注解 封装对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClientByAnno {
/**
* 用户ID
*/
@Desensitive(strategy = DesensitizedType.USER_ID)
private Long userId;
/**
* 姓名
*/
@Desensitive(strategy = DesensitizedType.CHINESE_NAME)
private String userName;
/**
* 身份证号
*/
@Desensitive(strategy = DesensitizedType.ID_CARD)
private String idCardNum;
/**
* 邮箱
*/
@Desensitive(strategy = DesensitizedType.EMAIL)
private String email;
/**
* 电话
*/
@Desensitive(strategy = DesensitizedType.FIXED_PHONE)
private String mobile;
/**
* 手机号
*/
@Desensitive(strategy = DesensitizedType.MOBILE_PHONE)
private String phone;
/**
* 银行卡号
*/
@Desensitive(strategy = DesensitizedType.BANK_CARD)
private String bankCard;
}
7. Controller
@RestController
@RequestMapping(value = "desensController")
public class DesensController {
private static ClientByAnno clientByAnno;
@PostConstruct
public void init() {
clientByAnno = new ClientByAnno();
clientByAnno.setUserId(100568899L);
clientByAnno.setUserName("诸葛孔明");
clientByAnno.setIdCardNum("35016920000512554X");
clientByAnno.setEmail("1005887898@163.com");
clientByAnno.setMobile("010-88878888");
clientByAnno.setPhone("15288800369");
clientByAnno.setBankCard("6212262872369687441");
}
@GetMapping(value = "byAnno")
public ResultMessage<ClientByAnno> byAnno() {
ResultMessage<ClientByAnno> resultMessage = new ResultMessage<>();
resultMessage.setData(clientByAnno);
return resultMessage;
}
}