放弃fastjson拥抱jackson
放弃fastjson 拥抱jackson
背景
- 功能强大 好用;不亏是国人更懂国人;但是安全漏洞频发;生产项目总是告警 勒令修改 放弃使用。
- 坑爹玩意fastjson 漏洞太多 直接搞了fastjson2;虽然大部分兼容 远古项目还需要升级谁敢动呀。动了引发一些未知BUG那岂不是背锅侠(玩笑 该干还得干 挣得就是这个钱)
安全漏洞
- 产生原因:序列化和反序列化;无非就是反序列化的时候能够注入调用一些非法函数。攻击者可以构造特殊的序列化数据包,当目标系统尝试反序列化这些数据时,恶意代码就会被执行
替代方案
- 都是JSON 序列化框架 跟着spring大腿学习使用springboot 默认序列化框架jackson
- jackson 运行是占用内存较低 性能较好
jackjson 模块
- jackson-databind数据绑定
- jackson-core用于JSON解析和生成
- jackson-annotations 用于注解支持
1. 核心类
- ObjectMapper
2.JackJson 序列化API
普通对象
复杂对象
List
Map
以上序列化使用writeValueAsString方法均可以 暂无特别之处
日期处理
- 默认Date类型序列化成时间戳
- 代用ObjectMapper setDateFormat 方法设置日期序列化转换格式 (SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");)
4.Jackjson 反序列化API
- jackson通过将JSON字段的名称与Java对象中的getter和setter方法进行匹配,将JSON对象的字段映射到Java对象中的属性。
- Jackson删除了getter和setter方法名称的“ get”和“ set”部分,并将其余名称的第一个字符转换为小写。
4.1 普通字符串反序列化(都有现成api暂不提供示例)
Jackjson 支持输入json字符串反序列化
Jackjson 支持输入文件反序列化
Jackjson 支持输入字节流反序列化
Jackjson 支持输入url反序列化
Jackjson 支持输入字节数组反序列化
4.2 JSON 数组字符串反序列化(反序列化的时候需要提供TypeReference类型)
JSON 数组字符串 -> Map集合
JSON 数组字符串 -> List集合
JSON 数组字符串 -> 对象数组
5. 支持自定义序列化反序列化示例
5.1 自定义序列化
SimpleModule version1Module = new SimpleModule();
//自定义序列化
version1Module.addSerializer(User.class, new JsonSerializer<User>() {
@Override
public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("username", user.getName());
jsonGenerator.writeNumberField("userage", user.getAge());
jsonGenerator.writeEndObject();
}
});
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(version1Module);
5.2 自定义反序列化
public class UserDeserializer extends StdDeserializer<User> {
public UserDeserializer() {
this(null);
}
public UserDeserializer(Class<?> vc) {
super(vc);
}
@Override
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
String name = node.get("name").asText();
int age = (Integer) ((IntNode) node.get("age")).numberValue();
return new User(name, age);
}
}
6. 树模型(JsonNode)
JsonNode 子类实现
- ObjectNode:对应JSON对象,包含一组键值对,可以通过.put(key, value)添加或更新属性。
- ArrayNode:对应JSON数组,包含一系列元素,可通过.add(value)插入新元素。
- TextNode、IntNode、LongNode、DoubleNode等:分别对应JSON中的字符串、整数、长整数、浮点数等基本类型值。
- BooleanNode、NullNode:分别对应JSON中的布尔值和null值。
7.Jackson注解使用
- @JsonIgnore 用于在序列化或反序列化过程中忽略某个属性
- @JsonIgnoreProperties (类级别注解) 用于批量指定在序列化或反序列化过程中应忽略的属性列表,特别适用于应对不明确或动态变化的输入 JSON 中可能存在但不应处理的额外字段 ignoreUnknown 为true 会自动忽略Java类型中没有对应的未知属性
- @JsonIgnoreType (类级别注解) 忽略某个类都不进行序列化
- @JsonProperty 用于指定类的属性在序列化和反序列化成 JSON 时所对应的键名
- @JsonFormat 用于指定日期、时间、日期时间以及其他数值类型在序列化和反序列化为 JSON 时的格式
- @JsonInclude
- VALUE_DEFAULT:这是默认策略,意味着所有字段都会被序列化,无论其值是否为 null
- NON_NULL:只有非 null 的字段会被序列化。这是最常用的策略,可以减少 JSON 输出的大小,尤其是在字段可能经常为 null 的情况下
- NON_ABSENT:除了 null 值,还会忽略那些未设置(未初始化)的字段。在 Java 中,这意味着除了 null,还会忽略默认值(如 0、false 或空字符串)
- NON_EMPTY:除了 null 和未初始化的值,还会忽略那些被认为是“空”的值,如空字符串、空集合或空数组
- @JsonAnyGetter 用于标记一个方法,获取除已知属性外的所有其他键值对。这些键值对通常存储在一个 Map 结构中,以便将它们作为一个附加的对象进行序列化。
- 动态属性支持:通过在返回 Map 的方法上使用 @JsonAnyGetter,您可以将一个对象的动态属性集合序列化为 JSON 对象的多个键值对。这些属性可能是在运行时添加的,或者基于某些条件动态生成的。
- 简化结构:避免为每个可能的动态属性单独声明字段和 getter/setter。只需维护一个 Map,即可处理任意数量和类型的额外属性。
- 兼容性与灵活性:当需要与未知或未来可能变化的数据结构交互时,@JsonAnyGetter 可确保 JSON 表示能够容纳未预定义的属性,从而提高系统的兼容性和适应性。
- @JsonSerialize 指定字段使用特定序列化策略
- @JsonDeserialize 指定字段使用特定反序列化策略
- @JsonAnySetter 用于处理反序列化过程中遇到的未知或额外的 JSON 键值对。当一个对象的JSON表示中包含无法直接映射到已声明属性的键时,这个注解可以帮助捕获并存储这些额外的数据 反序列化字段是Map类型未明确具体类型
- @JsonRawValue 指示Jackson在序列化时应将其原始值视为未经转义的 JSON 字符串,并直接嵌入到输出的 JSON 文档中
8. SpringBoot 如何不覆盖默认的Jackson而是拓展?
- 实现WebMvcConfigurer接口并覆盖extendMessageConverters方法
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.text.SimpleDateFormat;
import java.util.List;
@Configuration
public class JacksonConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter`<?>> converters) {
for (HttpMessageConverter<?>` converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
// 在这里配置你的 ObjectMapper
// 例如设置日期格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
}
}
}
- 使用Customizer接口扩展Jackson行为
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.text.SimpleDateFormat;
import java.util.List;
@Configuration
public class JacksonConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter`<?>> converters) {
for (HttpMessageConverter<?>` converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
// 在这里配置你的 ObjectMapper
// 例如设置日期格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
}
}
}