最近公司产品有个需求,需要对后台系统页面敏感字段进行脱敏。涉及的到页面很多,于是定义了一个基于注解的方式的通用脱敏工具类。
1、脱敏字段注解
/**
* 自定义脱敏注解,用在需要脱敏的字段上
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
DesensitizationRule value();
String replacement() default "";
}
在上述代码中,我们在 @Sensitive 注解中添加了一个 replacement 属性,用于指定脱敏替换字符串。在 desensitize 方法中,如果字段上的 @Sensitive 注解中指定了 replacement 属性,则使用该属性的值作为脱敏替换字符串;否则,使用脱敏规则中定义的默认替换字符串。
2、脱敏规则枚举
// 脱敏规则枚举
public enum DesensitizationRule {
PHONE("(\d{3})\d{4}(\d{4})", "$1****$2"),
ID_NUMBER("(\d{6})\d{8}(\w{4})", "$1********$2"),
NAME("(?<=.).", "*");
private final String regex;
private final String replacement;
DesensitizationRule(String regex, String replacement) {
this.regex = regex;
this.replacement = replacement;
}
public String getRegex() {
return regex;
}
public String getReplacement() {
return replacement;
}
}
上述代码中的正则表达式 (\d{6})\d{8}(\w{4}) 表示匹配身份证号码前6位数字、8位数字和最后4位字符的部分。 $1 和 $2 分别表示正则表达式中的第一个和第二个括号捕获的内容,用于保留前6位和最后4位的值,中间的8位数字用 ******** 替代。 使用这个正则表达式可以实现对身份证号码进行脱敏处理,保护敏感信息的隐私和安全。请根据具体的业务需求和隐私保护要求进行调整和扩展。
3、脱敏工具类
/**
* 脱敏工具类 ,可对对象、数组、集合 进行脱敏
*/
public class DesensitizationUtils {
// 对象脱敏方法
public static <T> T desensitize(T data) {
Field[] fields = data.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Sensitive.class)) {
field.setAccessible(true);
try {
Sensitive sensitive = field.getAnnotation(Sensitive.class);
DesensitizationRule rule = sensitive.value();
String replacement = sensitive.replacement().isEmpty() ? rule.getReplacement() : sensitive.replacement();
String value = (String) field.get(data);
if(null != value){
String desensitizedValue = value.replaceAll(rule.getRegex(), replacement);
field.set(data, desensitizedValue);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return data;
}
//数组脱敏
public static <T> T[] desensitize(T[] arr) {
for (int i = 0; i <arr.length ; i++) {
T data= arr[i];
desensitize(data);
}
return arr;
}
//集合脱敏
public static <T> Collection<T> desensitize(Collection<T> coll) {
Iterator<T> iterator = coll.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
desensitize(data);
}
return coll;
}
}
通用的脱敏方法,它通过遍历数据对象的字段,对带有@Sensitive注解的字段进行脱敏处理。脱敏规则和替换字符串可以通过注解参数进行指定,如果没有指定,则使用默认的脱敏规则和替换字符串。
代码的步骤如下:
- 获取数据的所有字段,通过调用data.getClass().getDeclaredFields()方法获得一个Field数组。
- 遍历字段数组,对每个字段进行处理。
- 判断字段是否带有@Sensitive注解,通过调用field.isAnnotationPresent(Sensitive.class)方法进行判断。
- 如果字段带有@Sensitive注解,则将字段设置为可访问,通过调用field.setAccessible(true)方法实现。
- 获取字段上的@Sensitive注解,通过调用field.getAnnotation(Sensitive.class)方法获取注解对象。
- 获取注解对象中的脱敏规则,通过调用sensitive.value()方法获取DesensitizationRule对象。
- 判断注解对象中的替换字符串是否为空,如果为空则使用脱敏规则中的默认替换字符串,通过调用sensitive.replacement().isEmpty()方法进行判断。
- 获取字段的值,通过调用field.get(data)方法将字段值转换为String类型。
- 使用脱敏规则中的正则表达式和替换字符串,通过调用value.replaceAll(rule.getRegex(), replacement)方法对字段值进行脱敏处理。
- 将脱敏后的值设置回字段,通过调用field.set(data, desensitizedValue)方法实现。
- 如果在设置字段值的过程中发生了IllegalAccessException异常,则打印异常信息
- 遍历完成后,返回处理后的数据。
4、调用工具类代码示例
// 用户类,用于示例
public static class UserInfo {
//姓名脱敏
@Sensitive( value = DesensitizationRule.NAME, replacement = "#")
private String name;
//手机号脱敏
@Sensitive(value = DesensitizationRule.PHONE)
private String phoneNumber;
//身份证号脱敏
@Sensitive(value = DesensitizationRule.ID_NUMBER)
private String idNumber;
public UserInfo(String name, String phoneNumber, String idNumber) {
this.name = name;
this.phoneNumber = phoneNumber;
this.idNumber = idNumber;
}
// 省略getter和setter方法
@Override
public String toString() {
return "UserInfo{" +
"name='" + name + ''' +
", phoneNumber='" + phoneNumber + ''' +
", idNumber='" + idNumber + ''' +
'}';
}
}
//测试类
public class TestDesensitization {
// 示例:对用户信息进行脱敏
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("张三", "13012345678", "110101199007280518");
System.out.println("脱敏前:" + userInfo.toString());
desensitize(userInfo);
System.out.println("脱敏后:" + userInfo.toString());
}