java实现数据脱敏:一个注解搞定接口返回数据脱敏
java实现数据脱敏:一个注解搞定接口返回数据脱敏使用 Spring 有以下方式:作为一个成熟的 Spring Web 应用程序。作为第三方 Web 框架,使用 Spring Frameworks 中间层。作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。用于远程使用。这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或
下午惬意时光,突然产品小姐姐走到我面前,打断我短暂的摸鱼time,企图与我进行深入交流,还好我早有防备没有闪,打开瑞star的点单页面,暗示没有一杯coffee解决不了的需求,需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作,我思考一反,表示某问题,马上安排。
思路1.要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范,思来想去,定义数据脱敏注解和数据脱敏逻辑的接口, 在返回类上,对需要进行脱敏的属性加上,并指定对应的脱敏策略操作。
2.接下来我只需要拦截控制器返回的数据,找到带有脱敏注解的属性操作即可,一开始打算用@ControllerAdvice去实现,但发现需要自己去反射类获取注解,当返回对象比较复杂,需要递归去反射,性能一下子就会降低,于是换种思路,我想到平时使用的@JsonFormat,跟我现在的场景很类似,通过自定义注解跟字段解析器,对字段进行自定义解析,tql
代码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;
}
Spring由哪些模块组成?
Spring 总共大约有 20 个模块, 由 1300 多个不同的文件构成。 而这些组件被分别整合在 核心容
器(Core Container) 、 AOP(Aspect Oriented Programming)和设备支持
(Instrmentation) 、 数据访问与集成(Data Access/Integeration) 、 Web 、 消息
(Messaging) 、 Test 等 6 个模块中。 以下是 spring 5 的模块结构图:
- spring core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,ioc)和依赖
注入(Dependency Injection,DI)功能。 - spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为
Bean。 - spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方
法。 - spring JDBC:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解
析, 用于简化JDBC。 - spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
- spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc
容器初始化和针对 Web 的 ApplicationContext。 - spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成
测试。
- 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
- 单例模式:Bean默认为单例模式。
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
- 模板方法:用来解决代码重复的问题。比如. RestTemplate JmsTemplate JpaTemplate。
- 观察者模式:定义对象是一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应
用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用
代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据
XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的
系统或应用。
Spring 提供了以下5种标准的事件:
- 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接
口中的refresh()方法时被触发。 - 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的
Start()方法开始/重新开始容器时触发该事件。 - 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext
的Stop()方法停止容器时触发该事件。 - 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容
器被关闭时,其管理的所有单例Bean都被销毁。 - 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结
束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被
发布以后,bean会自动被通知。
Spring 应用一般有以下组件:
接口 - 定义功能。
Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
Bean 配置文件 - 包含类的信息以及如何配置它们。
Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
用户程序 - 它使用接口。
使用 Spring 有以下方式:
作为一个成熟的 Spring Web 应用程序。
作为第三方 Web 框架,使用 Spring Frameworks 中间层。
作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java Objects)。
用于远程使用。