当前位置: 首页 > news >正文

如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可

1 背景

需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作

2 思路

①要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范。思来想去,定义数据脱敏注解和数据脱敏逻辑的接口, 在返回类上,对需要进行脱敏的属性加上,并指定对应的脱敏策略操作。

接下来我只需要拦截控制器返回的数据,找到带有脱敏注解的属性操作即可,一开始打算用 @ControllerAdvice 去实现,但发现需要自己去反射类获取注解。当返回对象比较复杂,需要递归去反射,性能一下子就会降低,于是换种思路,我想到平时使用的 @JsonFormat,跟我现在的场景很类似,通过自定义注解跟字段解析器,对字段进行自定义解析,tql。

3 实现代码

3.1自定义数据注解,并可以配置数据脱敏策略:

package com.wkf.workrecord.tools.desensitization;import java.lang.annotation.*;/*** 注解类* @author wuKeFan* @date 2023-02-20 09:36:39*/@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataMasking {DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;}

3.2 自定义 Serializer,参考 jackson 的 StringSerializer,下面的示例只针对 String 类型进行脱敏

DataMaskingOperation.class:

package com.wkf.workrecord.tools.desensitization;/*** 接口脱敏操作接口类* @author wuKeFan* @date 2023-02-20 09:37:48*/
public interface DataMaskingOperation {String MASK_CHAR = "*";String mask(String content, String maskChar);}

DataMaskingFunc.class:

package com.wkf.workrecord.tools.desensitization;import org.springframework.util.StringUtils;/*** 脱敏转换操作枚举类* @author wuKeFan* @date 2023-02-20 09:38:35*/
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;}}

DataMaskingSerializer.class:

package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;import java.io.IOException;
import java.util.Objects;/*** 自定义Serializer* @author wuKeFan* @date 2023-02-20 09:39:47*/
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) {return this.createSchemaNode("string", true);}public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {this.visitStringFormat(visitor, typeHint);}
}

3.3 自定义 AnnotationIntrospector,适配我们自定义注解返回相应的 Serializer

package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
import lombok.extern.slf4j.Slf4j;/*** @author wuKeFan* @date 2023-02-20 09:43:41*/
@Slf4j
public class DataMaskingAnnotationIntroSpector extends NopAnnotationIntrospector {@Overridepublic Object findSerializer(Annotated am) {DataMasking annotation = am.getAnnotation(DataMasking.class);if (annotation != null) {return new DataMaskingSerializer(annotation.maskFunc().operation());}return null;}}

3.4 覆盖 ObjectMapper:

package com.wkf.workrecord.tools.desensitization;import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;/*** 覆盖 ObjectMapper* @author wuKeFan* @date 2023-02-20 09:44:35*/
@Configuration(proxyBeanMethods = false)
public class DataMaskConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({Jackson2ObjectMapperBuilder.class})static class JacksonObjectMapperConfiguration {JacksonObjectMapperConfiguration() {}@Bean@PrimaryObjectMapper 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;}}}

3.5 返回对象加上注解:

package com.wkf.workrecord.tools.desensitization;import lombok.Data;import java.io.Serializable;/*** 需要脱敏的实体类* @author wuKeFan* @date 2023-02-20 09:35:52*/
@Data
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;}

4 测试

我们写一个Controller测试一下看是不是我们需要的效果

4.1 测试的Controller类DesensitizationController.class如下:

package com.wkf.workrecord.tools.desensitization;import com.biboheart.brick.model.BhResponseResult;
import com.wkf.workrecord.utils.ResultVOUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** 测试接口脱敏测试控制类* @author wuKeFan* @date 2022-06-21 17:23*/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/desensitization/")
public class DesensitizationController {@RequestMapping(value = "test", method = {RequestMethod.GET, RequestMethod.POST})public BhResponseResult<User> test() {User user = new User();user.setAge(1);user.setEmail("123456789@qq.com");user.setName("吴名氏");user.setId(1L);return ResultVOUtils.success(user);}}

4.2 PostMan接口请求,效果符合预期,如图:

http://www.lryc.cn/news/14387.html

相关文章:

  • python 之 海龟绘图(turtle)
  • RT-Thread SPI使用教程
  • shiro使用——整合spring
  • 2023-02-20 leetcode-AccountsMerge
  • 中国高速公路行业市场规模及未来发展趋势
  • 佳能iC MF645CX彩色激光多功能打印机报E302-0001故障码检修
  • 加密越来越简单——用JavaScript实现数据加密和解密
  • 线程池的使用场景
  • 图像分割算法
  • 《mysql技术内幕:innodb存储引擎》笔记
  • windows与linux系统ntp服务器配置
  • html常用font-family设置字体样式
  • 数据库事务AICD以及隔离级别
  • (4)VScode之ssh基础配置
  • springcloud-1环境搭建、service provider
  • 光谱仪工作过程及重要参数定义
  • W800|iot|HLK-W800-KIT-PRO|AliOS|阿里云| |官方demo|学习(1):板载AliOS系统快速上手
  • 字节终面,一道Linux题难住我了
  • 三、NetworkX工具包实战2——可视化【CS224W】(Datawhale组队学习)
  • 【MySQL】MySQL 架构
  • Python日期时间模块
  • 学以致用——植物信息录入1.0(selenium+pandas+os+tkinter)
  • 什么是压敏电阻
  • Leetcode.901 股票价格跨度
  • vue入门(四)组件基础,$emits简单用法
  • VBA提高篇_27 OptionBOX_CheckBox_Frame_Image_VBA附加控件
  • STM32开发(11)----CubeMX配置独立看门狗(IWDG)
  • 医疗方案 | 星辰天合入选“2022智慧新医信优秀解决方案”
  • 【系统服务实战】tomcat服务的安装实战
  • 【图文详解】Unity存储游戏数据的几种方法