太强了,一个注解搞定接口返回数据脱敏
往期热门文章:
1、isEmpty和isBlank的区别,至少一半人答不上来... 2、Lombok同时使⽤@Data和@Builder 的坑 3、IntelliJ IDEA快捷键大全 + 动图演示,建议收藏! 4、如何防止你的 jar 被反编译? 5、大公司为什么禁止SpringBoot项目使用Tomcat? juejin.cn/post/7110110794188062727
思路
代码
1. 自定义数据注解,并可以配置数据脱敏策略
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {
DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;
}
2. 自定义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);
}
}
3. 自定义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;
}
}
4. 覆盖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;
}
}
}
5. 返回对象加上注解
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;
}
往期热门文章:
1、计算机专业会不会成为下一个土木? 2、xxl-job惊艳的设计,怎能叫人不爱 3、ArrayList#subList这四个坑,一不小心就中招 4、面试官:大量请求 Redis 不存在的数据,从而影响数据库,该如何解决? 5、MySQL 暴跌! 6、超越 Xshell!号称下一代 Terminal 终端神器,用完爱不释手! 7、IDEA 官宣全新默认 UI,太震撼了!! 8、让你直呼「卧槽」的 GitHub 项目! 9、Kafka又笨又重,为啥不选Redis? 10、50多个高频免费 API 接口分享
评论