基于Jackson实现API接口数据脱敏的示例详解

 更新时间:2023年08月11日 11:30:51   作者:skywsp  
用户的一些敏感数据,例如手机号、邮箱、身份证等信息,在数据库以明文存储,但在接口返回数据给浏览器(或三方客户端)时,希望对这些敏感数据进行脱敏,所以本文就给大家介绍以恶如何利用Jackson实现API接口数据脱敏,需要的朋友可以参考下

一、背景

用户的一些敏感数据,例如手机号、邮箱、身份证等信息,在数据库以明文存储(加密存储见《基于Mybatis-Plus拦截器实现MySQL数据加解密》), 但在接口返回数据给浏览器(或三方客户端)时,希望对这些敏感数据进行脱敏。 同时在浏览器提交数据更新到后端接口时,后端接口需要判断敏感数据是否已脱敏,如果已脱敏则需要直接丢弃。

二、Jackson介绍

Jackson是SpringBoot默认的Json序列化和反序列化库,点击这里查看Jackson注解官方文档.

本文通过使用Jackson的@JsonSerialize注解实现序列化时脱敏操作,通过使用Jackson的@JsonDeserialize注解实现反序列化时脱敏数据检测并丢弃操作。

三、使用方法

该脱敏拦截器功能在wutong-base-api包(公司内部包)已经实现,如果您的项目已经依赖了base-api,就可以直接使用。

另外,在码云上有Demo案例,见: spring-jackson

基于wutong-base-api包的使用步骤如下。

1、添加wutong-base-api依赖

<dependency>
    <groupId>com.talkweb</groupId>
    <artifactId>wutong-base-api</artifactId>
    <version>请使用最新版本</version>
</dependency>

2、在yaml配置开关,启用加解密

spring:
  jackson:
    sensitive:
      # 序列化时是否对手机号进行脱敏,反序列化时是否过滤脱敏数据
      mobile: true
      # 序列化时是否对邮箱进行脱敏,反序列化时是否过滤脱敏数据
      email: true
      # 序列化时是否对身份证号进行脱敏,反序列化时是否过滤脱敏数据
      identity: true

3、定义VO类,使用Jackson注解实现数据脱敏

API接口出参(Rsp),敏感数据序列化时脱敏

public class UserRspVO {
    private Long id;
    private String name;
    @JsonSerialize(using = CustomerJackSon.MobileSerialize.class)
    private String mobile;
    @JsonSerialize(using = CustomerJackSon.EmailSerialize.class)
    private String email;
    @JsonSerialize(using = CustomerJackSon.IdentitySerialize.class)
    private String identity;
}

API接口入参(Req),过滤已脱敏的数据,直接丢弃

public class UserReqVO {
    private Long id;
    private String name;
    @JsonDeserialize(using = CustomerJackSon.MobileDeSerialize.class)
    private String mobile;
    @JsonDeserialize(using = CustomerJackSon.EmailDeSerialize.class)
    private String email;
    @JsonDeserialize(using = CustomerJackSon.IdentityDeSerialize.class)
    private String identity;
}

定义Controller接口

@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping
    public UserRspVO get() {
        UserRspVO rsp = new UserRspVO();
        rsp.setId(1L);
        rsp.setName("张三");
        rsp.setMobile("18866661234");
        rsp.setEmail("zhangsan@qq.com");
        rsp.setIdentity("434113199901015566");
        return rsp;
    }
    @PutMapping
    public UserRspVO put(@RequestBody UserReqVO req) {
        System.out.println(req);
        UserRspVO rsp = new UserRspVO();
        BeanUtils.copyProperties(req, rsp);
        return rsp;
    }
}

接口请求示例:

GET http://localhost:8080/user
入参:
无
出参:
{
  "id": "1",
  "name": "张三",
  "mobile": "188****1234", // 配置了mobile=true
  "email": "zhangsan@qq.com", // 配置了email=false
  "identity": "434113199901015566" // 未配置identity,默认为false
}
PUT http://127.0.0.1:8080/user
入参:
{
    "id": "12124",
    "name": "张三",
    "mobile": "188****1234",
    "email": "zh****@qq.com",
    "identity": "434***********5566"
}
Controller反序列化后接收到的数据:
{
    "id": "12124",
    "name": "张三",
    "mobile": null, // 配置了mobile=true
    "email": "zh****@qq.com", // 配置了email=false
    "identity": "434***********5566" // 未配置identity,默认为false
}

四、实现原理

1、SpringBoot定义变量实现脱敏开关

读取yaml配置

@ConfigurationProperties(prefix = "spring.jackson.sensitive")
public class JacksonProperties {
    private Boolean mobile;
    private Boolean email;
    private Boolean identity;
}

变量注入到JacksonConfig

@Configuration
@EnableConfigurationProperties(JacksonProperties.class)
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper(JacksonProperties props) {
        ObjectMapper mapper = new ObjectMapper();
        ContextAttributes attrs = ContextAttributes.getEmpty()
                .withSharedAttribute("mobile", props.getMobile())
                .withSharedAttribute("email", props.getEmail())
                .withSharedAttribute("identity", props.getIdentity());
        mapper.setDefaultAttributes(attrs);
        return mapper;
    }
}

2、实现序列化、反序列化逻辑

public class CustomerJackSon {
    /**
     * 手机号脱敏序列化
     */
    public static class MobileSerialize extends JsonSerializer<String> {
        @Override
        public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (null == value || value.length() != 11) {
                jsonGenerator.writeString(value);
                return;
            }
            Boolean mobile = (Boolean) serializerProvider.getAttribute("mobile");
            if (null == mobile || !mobile) {
                jsonGenerator.writeString(value);
                return;
            }
            jsonGenerator.writeString(value.substring(0, 3) + "****" + value.substring(7));
        }
    }
    /**
     * 手机号脱敏数据检测并丢弃
     */
    public static class MobileDeSerialize extends JsonDeserializer<String> {
        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
            String value = p.getText();
            if (null == value || value.length() != 11) {
                return value;
            }
            Boolean mobile = (Boolean) ctxt.getAttribute("mobile");
            if (null == mobile || !mobile) {
                return value;
            }
            if (value.contains("*")) {
                return null;
            }
            return value;
        }
    }
    /**
     * 邮箱脱敏序列化
     */
    public static class EmailSerialize extends JsonSerializer<String> {
        @Override
        public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (null == value || value.length() < 5) {
                jsonGenerator.writeString(value);
                return;
            }
            String[] split = value.split("@");
            if (split.length != 2 || split[0].length() < 2 || split[1].length() < 2) {
                jsonGenerator.writeString(value);
                return;
            }
            Boolean email = (Boolean) serializerProvider.getAttribute("email");
            if (null == email || !email) {
                jsonGenerator.writeString(value);
                return;
            }
            jsonGenerator.writeString(split[0].substring(0, 2) + "****@" + split[1]);
        }
    }
    /**
     * 邮箱脱敏数据检测并丢弃
     */
    public static class EmailDeSerialize extends JsonDeserializer<String> {
        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
            String value = p.getText();
            if (null == value || value.length() < 5) {
                return value;
            }
            Boolean email = (Boolean) ctxt.getAttribute("email");
            if (null == email || !email) {
                return value;
            }
            if (value.contains("*")) {
                return null;
            }
            return value;
        }
    }
    /**
     * 身份证脱敏序列化
     */
    public static class IdentitySerialize extends JsonSerializer<String> {
        @Override
        public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (null == value || value.length() != 18) {
                jsonGenerator.writeString(value);
                return;
            }
            Boolean identity = (Boolean) serializerProvider.getAttribute("identity");
            if (null == identity || !identity) {
                jsonGenerator.writeString(value);
                return;
            }
            jsonGenerator.writeString(value.substring(0, 3) + "***********" + value.substring(14));
        }
    }
    /**
     * 身份证脱敏数据检测并丢弃
     */
    public static class IdentityDeSerialize extends JsonDeserializer<String> {
        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
            String value = p.getText();
            if (null == value || value.length() != 18) {
                return value;
            }
            Boolean identity = (Boolean) ctxt.getAttribute("identity");
            if (null == identity || !identity) {
                return value;
            }
            if (value.contains("*")) {
                return null;
            }
            return value;
        }
    }
}

以上就是基于Jackson实现API接口数据脱敏的示例详解的详细内容,更多关于Jackson API接口数据脱敏的资料请关注脚本之家其它相关文章!

相关文章

  • FreeMarker配置(Configuration)

    FreeMarker配置(Configuration)

    所有与该configuration 对象关联的模版实例都就可以通过获得to_upper 转换器,company 来获得字符串,因此你不需要再一次次的往root 中添加这些变量了。如果你往root 添加同名的变量,那么你新添加的变量将会覆盖之前的共享变量。
    2016-04-04
  • Java参数按值传递示例演示

    Java参数按值传递示例演示

    在Java中,方法参数的传递方式实际上是按值传递,接下来通过本文给大家介绍了Java参数按值传递示例演示,需要的朋友可以参考下
    2023-09-09
  • 【spring-boot】快速构建spring-boot微框架的方法

    【spring-boot】快速构建spring-boot微框架的方法

    本篇文章主要介绍了【spring-boot】快速构建spring-boot微框架的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • SpringBoot集成Curator实现Zookeeper基本操作的代码示例

    SpringBoot集成Curator实现Zookeeper基本操作的代码示例

    Zookeeper是一个Apache开源的分布式的应用,为系统架构提供协调服务,ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户,本文给大家介绍了SpringBoot集成Curator实现Zookeeper基本操作,需要的朋友可以参考下
    2024-05-05
  • Java软件编程培训机构靠谱吗

    Java软件编程培训机构靠谱吗

    随着网络信息化的快速发展,Java培训受到越来越多人的青睐,目前Java工程师的薪资水平在不断攀升,但是有好多企业还是招不到合适的人才,为什么呢
    2017-04-04
  • SpringBoot3整合WebSocket详细指南

    SpringBoot3整合WebSocket详细指南

    SpringBoot 3 整合 WebSocket 提供了一种高效的实时通信解决方案,通过本文的配置和示例,可以快速实现,感兴趣的哦朋友跟随小编一起看看吧
    2024-12-12
  • Java入门案列之猜拳小游戏

    Java入门案列之猜拳小游戏

    这篇文章主要为大家详细介绍了Java入门案列之猜拳小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • 在navicat中导入mysql数据库详细步骤(即.sql后缀的数据库)

    在navicat中导入mysql数据库详细步骤(即.sql后缀的数据库)

    Navicat是MySQL非常好用的可视化管理工具,功能非常强大,能满足我们日常数据库开发的所有需求,下面这篇文章主要给大家介绍了关于如何在navicat中导入mysql数据库(即.sql后缀的数据库)的相关资料,需要的朋友可以参考下
    2023-04-04
  • windows下java环境变量的设置方法

    windows下java环境变量的设置方法

    在“系统变量”中,设置3项属性,JAVA_HOME,PATH,CLASSPATH(大小写无所谓),若已存在则点击“编辑”,不存在则点击“新建”
    2013-09-09
  • Java8如何利用Lambda快速生成map、多层嵌套map

    Java8如何利用Lambda快速生成map、多层嵌套map

    这篇文章主要介绍了Java8如何利用Lambda快速生成map、多层嵌套map问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09

最新评论