- 自定义数据注解,并可以配置数据脱敏策略
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
}
- 自定义Serializer,参考jackson的StringSerializer,下面的示例只针对String类型进行脱敏.
public interface DataMaskingOperation {
String MASK_CHAR = "*";
String mask(String content, String maskChar);
}
public enum DataMaskingFunc {
/**
* 脱敏转换器
*/
NO_MASK((str, maskChar) -> {
return str;
}),
ALL_MASK((str, maskChar) -> {
if (StringUtils.hasLength(str)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
sb.append(StringUtils.hasLength(maskChar) ? maskChar : DataMaskingOperation.MASK_CHAR);
}
return sb.toString();
} else {
return str;
}
});
private final DataMaskingOperation operation;
private DataMaskingFunc(DataMaskingOperation operation) {
this.operation = operation;
}
public DataMaskingOperation operation() {
return this.operation;
}
}
public final class DataMaskingSerializer extends StdScalarSerializer<Object> {
private final DataMaskingOperation operation;
public DataMaskingSerializer() {
super(String.class, false);
this.operation = null;
}
public DataMaskingSerializer(DataMaskingOperation operation) {
super(String.class, false);
this.operation = operation;
}
public boolean isEmpty(SerializerProvider prov, Object value) {
String str = (String)value;
return str.isEmpty();
}
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (Objects.isNull(operation)) {
String content = DataMaskingFunc.ALL_MASK.operation().mask((String) value, null);
gen.writeString(content);
} else {
String content = operation.mask((String) value, null);
gen.writeString(content);
}
}
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
this.serialize(value, gen, provider);
}
public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
return this.createSchemaNode("string", true);
}
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
this.visitStringFormat(visitor, typeHint);
}
}
- 自定义AnnotationIntrospector,适配我们自定义注解返回相应的Serializer
@Slf4j
public class DataMaskingAnnotationIntrospector extends NopAnnotationIntrospector {
@Override
public Object findSerializer(Annotated am) {
DataMasking annotation = am.getAnnotation(DataMasking.class);
if (annotation != null) {
return new DataMaskingSerializer(annotation.maskFunc().operation());
}
return null;
}
}
- 覆盖ObjectMapper.
@Configuration(
proxyBeanMethods = false
)
public class DataMaskConfiguration {
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Jackson2ObjectMapperBuilder.class})
static class JacksonObjectMapperConfiguration {
JacksonObjectMapperConfiguration() {
}
@Bean
@Primary
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
objectMapper.setAnnotationIntrospector(newAi);
return objectMapper;
}
}
}
- 返回对象加上注解
public class User implements Serializable {
/**
* 主键ID
*/
private Long id;
/**
* 姓名
*/
@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 邮箱
*/
@DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
private String email;
}
======================================
更简单的数据脱敏
导入依赖
<!-- hutool工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.6.7</version>
</dependency>
自定义注解 - Desensitize
import com.asurplus.common.enums.DesensitizeRuleEnums;
import com.asurplus.common.jackson.SensitiveJsonSerializer;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据脱敏注解
*
* @Author Asurplus
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Desensitize {
/**
* 脱敏规则
*/
DesensitizeRuleEnums rule();
}
脱敏规则枚举类
import cn.hutool.core.util.DesensitizedUtil;
import lombok.AllArgsConstructor;
import java.util.function.Function;
/**
* 脱敏策略
*
* @author Asurplus
*/
@AllArgsConstructor
public enum DesensitizeRuleEnums {
/**
* 用户id脱敏
*/
USER_ID(s -> String.valueOf(DesensitizedUtil.userId())),
/**
* 中文姓名脱敏
*/
CHINESE_NAME(DesensitizedUtil::chineseName),
/**
* 身份证脱敏
*/
ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),
/**
* 固定电话
*/
FIXED_PHONE(DesensitizedUtil::fixedPhone),
/**
* 手机号脱敏
*/
MOBILE_PHONE(DesensitizedUtil::mobilePhone),
/**
* 地址脱敏
*/
ADDRESS(s -> DesensitizedUtil.address(s, 8)),
/**
* 电子邮箱脱敏
*/
EMAIL(DesensitizedUtil::email),
/**
* 密码脱敏
*/
PASSWORD(DesensitizedUtil::password),
/**
* 中国车牌脱敏
*/
CAR_LICENSE(DesensitizedUtil::carLicense),
/**
* 银行卡脱敏
*/
BANK_CARD(DesensitizedUtil::bankCard);
/**
* 可自行添加其他脱敏策略
*/
private final Function<String, String> desensitize;
public Function<String, String> desensitize() {
return desensitize;
}
}
数据脱敏 JSON 序列化工具
import com.asurplus.common.annotation.Desensitize;
import com.asurplus.common.enums.DesensitizeRuleEnums;
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 java.io.IOException;
import java.util.Objects;
/**
* 数据脱敏JSON序列化工具
*
* @Author Asurplus
*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
private DesensitizeRuleEnums rule;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(rule.desensitize().apply(value));
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
Desensitize annotation = property.getAnnotation(Desensitize.class);
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {
this.rule = annotation.rule();
return this;
}
return prov.findValueSerializer(property.getType(), property);
}
}
测试
@Data
static class UserInfo {
// 用户id
private Long id;
// 姓名
@Desensitize(rule = DesensitizeRuleEnums.CHINESE_NAME)
private String name;
// 邮箱
@Desensitize(rule = DesensitizeRuleEnums.EMAIL)
private String email;
// 电话
@Desensitize(rule = DesensitizeRuleEnums.MOBILE_PHONE)
private String phone;
}