在实际的项目当中经常会有产品要求某个返回的字段需要数据脱敏,比如手机号和其它敏感字段,本文就讲解如何使用一个自定义注解来进行数据的脱敏
自定义脱敏注解
/** * @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;}
返回截图:
总结:
使用自定义注解的方式来进行数据的脱敏可以减少在代码中的代码行数,更具可读性,符合开闭原则。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/105982.html