太強了,一個注解搞定接口返回數(shù)據(jù)脫敏
來源:juejin.cn/post/
7110110794188062727
思路 代碼 1. 自定義數(shù)據(jù)注解,并可以配置數(shù)據(jù)脫敏策略 2. 自定義Serializer,參考jackson的StringSerializer,下面的示例只針對String類型進行脫敏 3. 自定義AnnotationIntrospector,適配我們自定義注解返回相應的Serializer 4. 覆蓋ObjectMapper 5. 返回對象加上注解
下午愜意時光,突然產(chǎn)品小姐姐走到我面前,打斷我短暫的摸魚time,企圖與我進行深入交流,還好我早有防備沒有閃,打開瑞star的點單頁面,暗示沒有一杯coffee解決不了的需求,需求是某些接口返回的信息,涉及到敏感數(shù)據(jù)的必須進行脫敏操作,我思考一反,表示某問題,馬上安排。
思路
1.要做成可配置多策略的脫敏操作,要不然一個個接口進行脫敏操作,重復的工作量太多,很顯然違背了“多寫一行算我輸”的程序員規(guī)范,思來想去,定義數(shù)據(jù)脫敏注解和數(shù)據(jù)脫敏邏輯的接口, 在返回類上,對需要進行脫敏的屬性加上,并指定對應的脫敏策略操作。
2.接下來我只需要攔截控制器返回的數(shù)據(jù),找到帶有脫敏注解的屬性操作即可,一開始打算用@ControllerAdvice去實現(xiàn),但發(fā)現(xiàn)需要自己去反射類獲取注解,當返回對象比較復雜,需要遞歸去反射,性能一下子就會降低,于是換種思路,我想到平時使用的@JsonFormat,跟我現(xiàn)在的場景很類似,通過自定義注解跟字段解析器,對字段進行自定義解析,tql
代碼
1. 自定義數(shù)據(jù)注解,并可以配置數(shù)據(jù)脫敏策略
@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 {
/**
* 脫敏轉(zhuǎn)換器
*/
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;
}
程序汪資料鏈接
堪稱神級的Spring Boot手冊,從基礎入門到實戰(zhàn)進階
臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!
臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!
字節(jié)跳動總結(jié)的設計模式 PDF 火了,完整版開放下載!
歡迎添加程序汪個人微信 itwang009 進粉絲群或圍觀朋友圈
評論
圖片
表情
