Fastjson实现数据脱敏

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;
	}
}

8. 结果

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加把劲骑士RideOn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值