创建Spring Boot项目添加以下依赖
org.springframework.boot spring-boot-starter-web cn.hutool hutool-core 5.7.22
然后定义Sensitive 注解,@JacksonAnnotationsInside的作用是将@JacksonAnnotation标注的注解作为一个组合注解,这里使用@JacksonAnnotationsInside在@Sensitive 注解中组合@JsonSerialize注解,这样就可以使用JsonSerialize的功能,并且可以拓展自定义属性和隐式得指定自定义序列化器SensitiveSerializer了。
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializer.class)
public @interface Sensitive {
Type value();
int startInclude() default 0;
int endExclude() default 0;
int overlayRepeat() default 4;
public enum Type {
MOBILE,
CHINESE_NAME,
ID_CARD,
FIXED_PHONE,
ADDRESS,
EMAIL,
BANK_CARD,
CUSTOM_HIDE,
CUSTOM_RETAIN_HIDE,
CUSTOM_OVERLAY,
}
}
2. 脱敏工具类
创建脱敏工具类SensitiveUtil ,统一封装数据脱敏的方法。
public class SensitiveUtil {
public static String handlerMobile(String mobile) {
if(StringUtil.isEmpty(mobile)){
return null;
}
return hide(mobile,3,mobile.length() - 4);
}
public static String handlerPhone(String phone) {
if(StringUtil.isEmpty(phone)){
return null;
}
return overlay(phone,StringPool.ASTERISK,1,0,phone.length() - 4);
}
public static String handlerIdCard(String idNum) {
if(StringUtil.isEmpty(idNum)){
return null;
}
return hide(idNum,6,idNum.length() - 4);
}
public static String handlerBankCard(String cardNum) {
if(StringUtil.isEmpty(cardNum)){
return null;
}
return hide(cardNum,6,cardNum.length() - 4);
}
public static String handlerAddress(String address) {
if(StringUtil.isEmpty(address)){
return null;
}
return overlay(address,StringPool.ASTERISK,4,9,address.length());
}
public static String handlerUsername(String username) {
if(StringUtil.isEmpty(username)){
return null;
}
return hide(username,1,username.length());
}
public static String handlerFixedPhone(final String num) {
if(StringUtil.isEmpty(num)){
return null;
}
return overlay(num, StringPool.ASTERISK,4, 0,num.length()-4);
}
public static String handlerEmail(final String email) {
if(StringUtil.isEmpty(email)){
return null;
}
final int index = StringUtil.indexOf(email, StringPool.AT_CHAR);
if (index <= 1) {
return email;
} else {
return hide(email, 1, index);
}
}
public static String overlay(String str, String overlay,int overlayRepeat, int start, int end) {
if (StringUtil.isEmpty(str)) {
return StringUtil.str(str);
}
if (StringUtil.isEmpty(overlay)) {
overlay = StringPool.EMPTY;
}
final int len = str.length();
if (start < 0) {
start = 0;
}
if (start > len) {
start = len;
}
if (end < 0) {
end = 0;
}
if (end > len) {
end = len;
}
if (start > end) {
final int temp = start;
start = end;
end = temp;
}
return str.substring(0, start) + StringUtil.repeat(overlay, overlayRepeat) + str.substring(end);
}
public static String hide(String str, int startInclude, int endExclude) {
return hide(str, startInclude, endExclude,StringPool.ASTERISK_CHAR);
}
public static String hide(String str, int startInclude, int endExclude, char replacedChar) {
if (StringUtil.isEmpty(str)) {
return StringUtil.str(str);
}
final int strLength = str.length();
if (startInclude > strLength) {
return StringUtil.str(str);
}
if (endExclude > strLength) {
endExclude = strLength;
}
if (startInclude > endExclude) {
// 如果起始位置大于结束位置,不替换
return StringUtil.str(str);
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++) {
if (i >= startInclude && i < endExclude) {
chars[i] = replacedChar;
} else {
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
}
3. 自定义Jackson序列化器
参考Jackson自带的StringSerializer,继承JsonSerializer,重写serialize方法,自定义Jackson序列化器SensitiveSerializer针对String类型数据进行数据脱敏处理。
public class SensitiveSerializer extends JsonSerializer4. 测试使用implements ContextualSerializer { private Sensitive.Type type; private int startInclude; private int endExclude; private int overlayRepeat; public SensitiveSerializer() {} public SensitiveSerializer(final Sensitive sensitive) { this.type = sensitive.value(); this.startInclude = sensitive.startInclude(); this.endExclude = sensitive.endExclude(); this.overlayRepeat = sensitive.overlayRepeat(); } @Override public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if(ObjectUtil.isEmpty(value)){ gen.writeString(value); return; }else { switch (this.type) { case MOBILE: gen.writeString(SensitiveUtil.handlerMobile(value)); break; case ID_CARD: gen.writeString(SensitiveUtil.handlerIdCard(value)); break; case BANK_CARD: gen.writeString(SensitiveUtil.handlerBankCard(value)); break; case CHINESE_NAME: gen.writeString(SensitiveUtil.handlerUsername(value)); break; case FIXED_PHONE: gen.writeString(SensitiveUtil.handlerFixedPhone(value)); break; case ADDRESS: gen.writeString(SensitiveUtil.handlerAddress(value)); break; case EMAIL: gen.writeString(SensitiveUtil.handlerEmail(value)); break; case CUSTOM_HIDE: gen.writeString(SensitiveUtil.hide(value,startInclude,endExclude)); break; case CUSTOM_RETAIN_HIDE: gen.writeString(SensitiveUtil.hide(value,startInclude,(value.length()-endExclude))); break; case CUSTOM_OVERLAY: gen.writeString(SensitiveUtil.overlay(value, StringPool.ASTERISK,overlayRepeat,startInclude,endExclude)); break; default: gen.writeString(value); } } } @Override public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException { if(Objects.isNull(property)){ return prov.getDefaultNullValueSerializer(); } if(Objects.equals(property.getType().getRawClass(), String.class)){ Sensitive sensitive = property.getAnnotation(Sensitive.class); if (Objects.isNull(sensitive)) { sensitive = property.getContextAnnotation(Sensitive.class); } if (Objects.nonNull(sensitive)) { return new SensitiveSerializer(sensitive); } } return prov.findValueSerializer(property.getType(), property); //Sensitive sensitive = property.getAnnotation(Sensitive.class); //type = sensitive.value(); //return this; } }
创建实体类Student
public class Student {
@Sensitive(value = Sensitive.Type.CHINESE_NAME)
private String name;
@Sensitive(value = Sensitive.Type.ID_CARD)
private String idCard;
@Sensitive(value = Sensitive.Type.BANK_CARD)
private String bankCard;
@Sensitive(value = Sensitive.Type.FIXED_PHONE)
private String fixedPhone;
@Sensitive(value = Sensitive.Type.ADDRESS)
private String address;
@Sensitive(value = Sensitive.Type.EMAIL)
private String email;
@Sensitive(value = Sensitive.Type.CUSTOM_RETAIN_HIDE,startInclude = 3,endExclude = 10)
private String remark;
public Student() {
}
public String getBankCard() {
return bankCard;
}
public void setBankCard(String bankCard) {
this.bankCard = bankCard;
}
public String getFixedPhone() {
return fixedPhone;
}
public void setFixedPhone(String fixedPhone) {
this.fixedPhone = fixedPhone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Student(String name, String idCard, String bankCard, String fixedPhone, String address, String email, String remark) {
this.name = name;
this.idCard = idCard;
this.bankCard = bankCard;
this.fixedPhone = fixedPhone;
this.address = address;
this.email = email;
this.remark = remark;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
}
定义接口/test/student
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/student")
public Student getStudent(){
Student student = new Student();
student.setName("张三三");
student.setIdCard("44082199612054343");
student.setBankCard("62173300255879654448");
student.setFixedPhone("3110026");
student.setAddress("广东省广州市天河区");
student.setEmail("1258398545@qq.com");
student.setRemark("sadhaonsdoasnodnaonodsn是大祭司大祭司你");
return student;
}
}
浏览器请求接口/test/student,查看接口返回的数据



