文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Jackson在Spring Boot高级应用技巧【Long精度丢失, @JsonValue, 数据脱敏】

2024-11-28 16:00

关注
@RestController
@RequestMapping("/longs")
public class LongController {


  @GetMapping("")
  public Map getData() {
    return Map.of("code", 0, "data", 123456789012345678L) ;
  }
}

接口返回了一个17位的Long类型数据,我们先直接通过浏览器访问,结果如下:

图片图片

我们看到了2个结果,浏览器显示的是正确的,但是通过 Network 查看数据错误,接下来,我们再通过Ajax获取数据。

function getData() {
  axios.get('http://localhost:8080/longs')
    .then(resp => {
      console.log(resp.data) ;
    }).catch((error) => {
      console.log(error) ;
    });
}

图片图片

在这种前后端分离的架构下,千万的小心,如果你返回的Long类型超过了前端number的范围,那么将出现该问题。接下来要解决也是非常的简单,我们只需要进行简单的配置即可。

@Component
public class PackMapperCustomizer implements Jackson2ObjectMapperBuilderCustomizer {


  @Override
  public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
    jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance) ;
  }
}

这里我们将Long类型转换为String类型。再次运行上面的代码

图片图片

通过自定义 Jackson2ObjectMapperBuilderCustomizer 属于全局的,项目中所有的Long都会被转换为String进行输出。这可能不是我们希望的,我们可能只是希望有针对的处理,这时候我们就可以通过注解的方式了,如下示例:

public class User {


  @JsonSerialize(using = ToStringSerializer.class)
  private Long id ;
  private String name ;
  // getters, setters
}

通过上面注解的方式就能针对性的控制输出字段类型。

2.2 @JsonValue序列化单个属性值

@JsonValue注解用于指示一个方法,该方法将被用来转换对象为JSON值。通常,这个方法会返回一个基本类型或其包装类的实例,或者是其他可以直接序列化为JSON的对象。使用 @JsonValue 可以简化对象到JSON的转换过程,使得特定属性或计算结果能够直接作为JSON输出,而无需定义复杂的序列化逻辑。

现在有,如下的枚举类:

public enum PaymentStatus {


  NO_PAY(0, "未支付"), PAID(1, "已支付") ;
  
  PaymentStatus(int code, String desc) {
    this.code = code ;
    this.desc = desc ;
  }
  private Integer code ;
  private String desc ;
  // getters, setters
}

当接口返回该枚举类时,显示如下:

@GetMapping("")
public PaymentStatus status() {
  return PaymentStatus.PAID ;
}

图片图片

输出了枚举的字符串形式,如果我们要显示具体的code或者是desc,那么我们就可以使用 @JsonValue 注解。

@JsonValue
private String desc ;

在需要输出的字段上添加 @JsonValue 注解即可。

图片图片

如果该注解应用到一个普通的Java Bean对象中的某个属性时,如下示例:

public class User {


  @JsonSerialize(using = ToStringSerializer.class)
  private Long id ;
  @JsonValue
  private String name ;
}

这时候接口输出的将是name属性对应的值。

图片图片


2.3 自定义注解

有些时候,我们可能对输出的某些字段要做特殊的处理在输出到前端,比如:身份证号,电话等信息,在前端展示的时候我们需要进行脱敏处理,这时候通过自定义注解就非常的有用了。在Jackson中要自定义注解,我们可以通过@JacksonAnnotationsInside注解来实现,如下示例:

自定义注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializer.class)
public @interface Sensitive {


  int start() default 0 ;
  
  int end() default 0 ;
  
  String mask() default "*" ;
}

自定义序列化处理器SensitiveSerializer

public class SensitiveSerializer extends JsonSerializer implements ContextualSerializer {


  private Sensitive sensitive;


  @Override
  public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    String val = value;
    if (sensitive != null && StringUtils.hasLength(val)) {
      String m = sensitive.mask();
      int start = sensitive.start();
      int end = sensitive.end();
      int totalLength = value.length();


      if (totalLength <= 2) {
        val = totalLength == 1 ? value + m : value.substring(0, 1) + m;
      } else if (totalLength <= 6) {
        val = value.substring(0, 1) + String.join("", Collections.nCopies(totalLength - 2, m))
            + value.substring(totalLength - 1);
      } else {
        int prefixLength = Math.min(start, totalLength - 1);
        int suffixLength = Math.min(end, totalLength - 1);


        if (prefixLength > totalLength) {
          prefixLength = totalLength / 2;
        }
        if (suffixLength > totalLength) {
          suffixLength = totalLength / 2;
        }


        int maskLength = Math.max(0, totalLength - (prefixLength + suffixLength));
        if (maskLength == 0) {
          prefixLength -= 2;
          suffixLength -= 2;
          maskLength = Math.max(2, totalLength - (prefixLength + suffixLength));
        }


        prefixLength = Math.min(prefixLength, totalLength - 1);
        suffixLength = Math.min(suffixLength, totalLength - 1);


        maskLength = totalLength - prefixLength - suffixLength;


        val = value.substring(0, prefixLength) + String.join("", Collections.nCopies(maskLength, m))
            + value.substring(totalLength - suffixLength);
      }
    }
    gen.writeString(val);
  }


  @Override
  public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property)
      throws JsonMappingException {
    sensitive = property.getAnnotation(Sensitive.class);
    return this;
  }
}

接下来,在输出的Java Bean中使用上面的注解。

public class User {


  @JsonSerialize(using = ToStringSerializer.class)
  private Long id ;
  private String name ;
  @Sensitive(start = 6, end = 4)
  private String idCard ;
  @Sensitive(start = 4, end = 3)
  private String phone ;
  // getters, setters
}

最后,在前端展示结果如下:

图片

敏感数据得到了脱敏处理。

来源:Spring全家桶实战案例源码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯