在实际的项目当中经常会有产品要求某个返回的字段需要数据脱敏,比如手机号和其它敏感字段,本文就讲解如何使用一个自定义注解来进行数据的脱敏
自定义脱敏注解
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:44
* @description
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
}
接口:
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:44
* @description
*/
public interface DataMaskingOperation {
char MASK_CHAR = '*';
String mask(String content, Character maskChar);
}
枚举:
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:44
* @description
*/
public enum DataMaskingFunc {
/**
* 脱敏转换器
*/
NO_MASK((str, maskChar) -> {
return str;
}),
PART_MASK((str, maskChar) -> {
if (StringUtils.hasLength(str) && str.length()>=5) {
StringBuilder sb = new StringBuilder();
sb.append(str);
for (int i = 2; i < str.length()-2; i++) {
sb.setCharAt(i,StringUtils.hasLength(String.valueOf(maskChar)) ? maskChar : DataMaskingOperation.MASK_CHAR);
}
return sb.toString();
} else {
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(String.valueOf(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;
}
}类:
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:47
* @description
*/
@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;
}
}
}
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:46
* @description
*/
@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;
}
}
/**
* @author WuSong
* @version 1.0
* @date 2022/8/29 11:45
* @description
*/
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;
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
String str = (String)value;
return str.isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (Objects.isNull(operation)) {
String content = DataMaskingFunc.PART_MASK.operation().mask((String) value, '*');
gen.writeString(content);
} else {
String content = operation.mask((String) value, '*');
gen.writeString(content);
}
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
this.serialize(value, gen, provider);
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
return this.createSchemaNode("string", true);
}
@Override
public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
this.visitStringFormat(visitor, typeHint);
}
}
具体使用:
/** * @Author WuSong * @Date 2022-08-02 11:39 */ @Data public class FindCouponCertificatesPageResponse {/** * 手机号码 */ @ApiModelProperty("手机号码") @DataMasking(maskFunc = DataMaskingFunc.PART_MASK) private String phone;}
返回截图:
![]()
总结:
使用自定义注解的方式来进行数据的脱敏可以减少在代码中的代码行数,更具可读性,符合开闭原则。
版权声明:本文为qq_41625866原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。