当前位置:

首页 > 编程开发 > Java怎么用Jackson序列化实现数据脱敏

Java怎么用Jackson序列化实现数据脱敏

1.背景在项目中有些敏感信息不能直接展示,比如客户手机号、身份证、车牌号等信息,展示时均需要进行数据脱敏,防止泄露客户隐私。脱敏即是对数据的部分信息用脱敏符号(*)处理。2.目标在服务端返回数据时,利用Jackson序列化完成数据脱敏,达到对敏感信息脱敏展示。降低重复开发量,提升开发效率形成统一有效的脱敏规则可基于重写默认脱敏实现的desensitize方法,实现可扩展、可自定义的个性化业务场景的脱敏需求3.主要实现3.1基于Jackson的自定义脱敏序列化实现StdSerializer:所有标准序列化程

本文目录

      1.背景

      在项目中有些敏感信息不能直接展示,比如客户手机号、身份证、车牌号等信息,展示时均需要进行数据脱敏,防止泄露客户隐私。脱敏即是对数据的部分信息用脱敏符号(*)处理。

      2.目标

      • 在服务端返回数据时,利用Jackson序列化完成数据脱敏,达到对敏感信息脱敏展示。

      • 降低重复开发量,提升开发效率

      • 形成统一有效的脱敏规则

      • 可基于重写默认脱敏实现的desensitize方法,实现可扩展、可自定义的个性化业务场景的脱敏需求

      3.主要实现

      3.1基于Jackson的自定义脱敏序列化实现

      StdSerializer:所有标准序列化程序所使用的基类,这个是编写自定义序列化程序所推荐使用的基类。

      ContextualSerializer: 是Jackson 提供的另一个序列化相关的接口,它的作用是通过字段已知的上下文信息定制JsonSerializer。

      package com.jd.ccmp.ctm.constraints.serializer;
      
      
      import com.fasterxml.jackson.core.JsonGenerator;
      import com.fasterxml.jackson.databind.BeanProperty;
      import com.fasterxml.jackson.databind.JsonSerializer;
      import com.fasterxml.jackson.databind.SerializerProvider;
      import com.fasterxml.jackson.databind.ser.ContextualSerializer;
      import com.fasterxml.jackson.databind.ser.std.StdSerializer;
      import com.jd.ccmp.ctm.constraints.Symbol;
      import com.jd.ccmp.ctm.constraints.annotation.Desensitize;
      import com.jd.ccmp.ctm.constraints.desensitization.Desensitization;
      import com.jd.ccmp.ctm.constraints.desensitization.DesensitizationFactory;
      import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization;
      
      
      
      
      import java.io.IOException;
      
      
      
      
      /**
       * 脱敏序列化器
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:10
       */
      public class ObjectDesensitizeSerializer extends StdSerializer implements ContextualSerializer {
          private static final long serialVersionUID = -7868746622368564541L;
          private transient Desensitization desensitization;
          protected ObjectDesensitizeSerializer() {
              super(Object.class);
          }
          public Desensitization getDesensitization() {
              return desensitization;
          }
          public void setDesensitization(Desensitization desensitization) {
              this.desensitization = desensitization;
          }
          @Override
          public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) {
      //获取属性注解
              Desensitize annotation = property.getAnnotation(Desensitize.class);
              return createContextual(annotation.desensitization());
          }
          @SuppressWarnings("unchecked")
          public JsonSerializer createContextual(Class> clazz) {
              ObjectDesensitizeSerializer serializer = new ObjectDesensitizeSerializer();
              if (clazz != DefaultDesensitization.class) {
                  serializer.setDesensitization((Desensitization) DesensitizationFactory.getDesensitization(clazz));
              }
              return serializer;
          }
          @Override
          public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
              Desensitization objectDesensitization = getDesensitization();
              if (objectDesensitization != null) {
                  try {
                      gen.writeObject(objectDesensitization.desensitize(value));
                  } catch (Exception e) {
                      gen.writeObject(value);
                  }
              } else if (value instanceof String) {
                  gen.writeString(Symbol.getSymbol(((String) value).length(), Symbol.STAR));
              } else {
                  gen.writeObject(value);
              }

      注:createContextual可以获得字段的类型以及注解。当字段拥有自定义注解时,取出注解中的值创建定制的序列化方式,这样在serialize方法中便可以得到这个值了。createContextual方法只会在第一次序列化字段时调用(因为字段的上下文信息在运行期不会改变),所以无需关心性能问题。

      3.2定义脱敏接口、以及工厂实现

      3.2.1脱敏器接口定义

      package com.jd.ccmp.ctm.constraints.desensitization;
      
      
      /**
       * 脱敏器
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 10:56
       */
      public interface Desensitization {
          /**
           * 脱敏实现
           *
           * @param target 脱敏对象
           * @return 脱敏返回结果
           */
          T desensitize(T target);
      }

      3.2.2脱敏器工厂实现

      package com.jd.ccmp.ctm.constraints.desensitization;
      
      import java.util.HashMap;
      import java.util.Map;
      
      
      /**
       * 工厂方法
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 10:58
       */
      public class DesensitizationFactory {
          private DesensitizationFactory() {
          }
          private static final Map, Desensitization> map = new HashMap<>();
      
      
      
      
          @SuppressWarnings("all")
          public static Desensitization getDesensitization(Class clazz) {
              if (clazz.isInterface()) {
                  throw new UnsupportedOperationException("desensitization is interface, what is expected is an implementation class !");
              }
              return map.computeIfAbsent(clazz, key -> {
                  try {
                      return (Desensitization) clazz.newInstance();
                  } catch (InstantiationException | IllegalAccessException e) {
                      throw new UnsupportedOperationException(e.getMessage(), e);
                  }
              });

      3.3常用的脱敏器实现

      3.3.1默认脱敏实现

      可基于默认实现,扩展实现个性化场景

      package com.jd.ccmp.ctm.constraints.desensitization;
      
      
      /**
       * 默认脱敏实现
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:01
       */
      public interface DefaultDesensitization extends Desensitization {
      }

      3.3.2手机号脱敏器

      实现对手机号中间4位号码脱敏

      package com.jd.ccmp.ctm.constraints.desensitization;
      import com.jd.ccmp.ctm.constraints.Symbol;
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      
      /**
       * 手机号脱敏器,保留前3位和后4位
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:02
       */
      public class MobileNoDesensitization implements DefaultDesensitization {
          /**
           * 手机号正则
           */
          private static final Pattern DEFAULT_PATTERN = Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}");
      
      
      
      
          @Override
          public String desensitize(String target) {
              Matcher matcher = DEFAULT_PATTERN.matcher(target);
              while (matcher.find()) {
                  String group = matcher.group();
                  target = target.replace(group, group.substring(0, 3) + Symbol.getSymbol(4, Symbol.STAR) + group.substring(7, 11));
              }
              return target;

      3.4注解定义

      通过@JacksonAnnotationsInside实现自定义注解,提高易用性

      package com.jd.ccmp.ctm.constraints.annotation;
      import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
      import com.fasterxml.jackson.databind.annotation.JsonSerialize;
      import com.jd.ccmp.ctm.constraints.desensitization.Desensitization;
      import com.jd.ccmp.ctm.constraints.serializer.ObjectDesensitizeSerializer;
      import java.lang.annotation.*;
      
      
      /**
       * 脱敏注解
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:09
       */
      @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @JacksonAnnotationsInside
      @JsonSerialize(using = ObjectDesensitizeSerializer.class)
      @Documented
      public @interface Desensitize {
          /**
           * 对象脱敏器实现
           */
          @SuppressWarnings("all")
          Class> desensitization();

      3.4.1默认脱敏注解

      package com.jd.ccmp.ctm.constraints.annotation;
      import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
      import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization;
      import java.lang.annotation.*;
      
      
      
      
      /**
       * 默认脱敏注解
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:14
       */
      @Target({ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      @JacksonAnnotationsInside
      @Desensitize(desensitization = DefaultDesensitization.class)
      @Documented
      public @interface DefaultDesensitize {

      3.4.2手机号脱敏注解

      package com.jd.ccmp.ctm.constraints.annotation;
      import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
      import com.jd.ccmp.ctm.constraints.desensitization.MobileNoDesensitization;
      import java.lang.annotation.*;
      
      
      /**
       * 手机号脱敏
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 11:18
       */
      @Target({ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      @JacksonAnnotationsInside
      @Desensitize(desensitization = MobileNoDesensitization.class)
      @Documented
      public @interface MobileNoDesensitize {
      }

      3.5定义脱敏符号

      支持指定脱敏符号,例如* 或是 ^_^

      package com.jd.ccmp.ctm.constraints;
      import java.util.stream.Collectors;
      import java.util.stream.IntStream;
      
      
      /**
       * 脱敏符号
       *
       * @author zhangxiaoxu15
       * @date 2022/2/8 10:53
       */
      public class Symbol {
          /**
           * '*'脱敏符
           */
          public static final String STAR = "*";
          private Symbol() {}
          /**
           * 获取符号
           *
           * @param number 符号个数
           * @param symbol 符号
           */
          public static String getSymbol(int number, String symbol) {
              return IntStream.range(0, number).mapToObj(i -> symbol).collect(Collectors.joining());
          }

      4.使用样例&执行流程剖析

      Java怎么用Jackson序列化实现数据脱敏

      程序类图

      Java怎么用Jackson序列化实现数据脱敏

      **执行流程剖析**

       1.调用JsonUtil.toJsonString()开始执行序列化

       2.识别属性mobile上的注解@MobileNoDesensitize(上文3.4.2)

       3.调用ObjectDesensitizeSerializer#createContextual(上文3.1 & 3.2),返回JsonSerializer

       4.调用手机号脱敏实现MobileNoDesensitization#desensitize(上文3.3.2)

       5.输出脱敏后的序列化结果,{"mobile":"133****5678"}

      不难发现核心执行流程是第3步,但是@MobileNoDesensitize与ObjectDesensitizeSerializer又是如何联系起来的呢?

      • 尝试梳理下引用链路:@MobileNoDesensitize -> @Desensitize -> @JsonSerialize -> ObjectDesensitizeSerializer

      • 但是,在ObjectDesensitizeSerializer的实现中,我们似乎却没有发现上述链路的直接调用关系

      • 这就不得不说下Jackson元注解的概念

      //**Jackson元注解**
      //1.提到元注解这个词,大家会想到@Target、@Retention、@Documented、@Inherited
      //2.Jackson也以同样的思路设计了@JacksonAnnotationsInside
      
      
      /**
       * Meta-annotation (annotations used on other annotations)
       * used for indicating that instead of using target annotation
       * (annotation annotated with this annotation),
       * Jackson should use meta-annotations it has.
       * This can be useful in creating "combo-annotations" by having
       * a container annotation, which needs to be annotated with this
       * annotation as well as all annotations it 'contains'.
       * 
       * @since 2.0
       */
      @Target({ElementType.ANNOTATION_TYPE})
      @Retention(RetentionPolicy.RUNTIME)
      @JacksonAnnotation
      public @interface JacksonAnnotationsInside
      {
      }

      正是通过”combo-annotations”(组合注解、捆绑注解)的机制,实现了指示Jackson应该使用其拥有的元注释,而不是使用目标注释,从而实现了自定义脱敏实现设计目标。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java jackson
      相关文章 更多
      Java单元测试入门:使用JUnit 5编写第一个测试用例
      Java单元测试入门:使用JUnit 5编写第一个测试用例

      面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

      Java变量未初始化报错解析:局部变量与成员变量默认值差异
      Java变量未初始化报错解析:局部变量与成员变量默认值差异

      深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

      详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

      Java中final与static final常量的定义及初始化规则
      Java中final与static final常量的定义及初始化规则

      深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

      Java抽象方法与接口方法的区别及选型建议
      Java抽象方法与接口方法的区别及选型建议

      深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

      Java实现HTTP请求与JSON解析的完整代码示例
      Java实现HTTP请求与JSON解析的完整代码示例

      本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

      Java递归出现StackOverflowError的原因分析及栈空间优化方案
      Java递归出现StackOverflowError的原因分析及栈空间优化方案

      详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

      Java空指针异常(NPE)的代码排查与修复实例
      Java空指针异常(NPE)的代码排查与修复实例

      针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

      selenium上传文件教程本地文件路径设置方法
      selenium上传文件教程本地文件路径设置方法

      深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

      针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

      为什么要购买正版软件?
      01 官方授权

      授权来源透明,订单与许可信息可随时查询。

      02 即时交付

      付款成功后自动发放激活信息,支持订单追踪。

      03 中文协助

      安装、激活、迁移与续费问题都有专人响应。

      04 长期可用

      提供升级提醒与授权管理,换机时也能轻松迁移。

      网站备案号:苏ICP备2026018738号-1 联系邮箱:bd@zhengruan.com 网站地图

      Copyright ©2018-2026