SpringBoot 接口数据如何实现优雅的脱敏?
实现方案:自定义注解 + Jackson序列化器
1.具体代码:
自定义脱敏注解:
package org.springblade.common.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springblade.common.config.SensitiveJsonSerializer;
import org.springblade.common.enums.SensitiveTypeEnum;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 脱敏注解
*
* @author hf
* @since 2023/6/14
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Desensitized {
/**
* 脱敏类型(规则)
*/
SensitiveTypeEnum type();
}
脱敏类型枚举类:支持用户名、手机号、身份证号、地址
package org.springblade.common.enums;
import java.util.function.Function;
/**
* 脱敏类型枚举
*
* @author hf
* @since
**/
public enum SensitiveTypeEnum {
/**
* 用户名
*/
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),
/**
* 身份证
*/
ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),
/**
* 手机号
*/
MOBILE_PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
/**
* 地址
*/
ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));
private final Function<String, String> desensitizer;
SensitiveTypeEnum(Function<String, String> desensitizer) {
this.desensitizer = desensitizer;
}
public Function<String, String> desensitizer() {
return desensitizer;
}
}
序列化注解自定义实现:
package org.springblade.common.config;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import org.springblade.common.annotation.Desensitized;
import org.springblade.common.enums.SensitiveTypeEnum;
import java.io.IOException;
import java.util.Objects;
/**
* 序列化注解自定义实现
* JsonSerializer<String>:指定String 类型,serialize()方法用于将修改后的数据载入
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveTypeEnum strategy;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(strategy.desensitizer().apply(value));
}
/**
* 获取属性上的注解属性
*/
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Desensitized annotation = property.getAnnotation(Desensitized.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
this.strategy = annotation.type();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
}
2.使用方法
添加注解@Desensitized
在需要进行脱敏的字段上添加@Desensitized注解,并选择具体的脱敏类型。示例如下:
/**
* 手机
*/
@Desensitized(type = SensitiveTypeEnum.MOBILE_PHONE)
private String phone;
/**
* 身份证号
*/
@Desensitized(type = SensitiveTypeEnum.ID_CARD)
private String idNo;
3.脱敏结果
3.1脱敏前数据:
{
"phone": "13924369234",
"idNo": "1210103199603282410",
}
3.2脱敏后数据:
{
"phone": "139****9234",
"idNo": "1210************10",
}