Fastjson已经连续几次爆出高危漏洞,和Structs一样,每次影响范围都比较广,殃及几乎所有的JAVA后台系统。为避免以后频繁地应急处理Fastjson的安全漏洞,痛定思痛,决定放弃Fastjson转投jackson的怀抱了。
在pom文件中添加jackson的依赖包,如下:
<properties> <jackson-version>2.9.9</jackson-version> </properties> ... <dependencyManagement> <dependencies> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson-version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson-version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson-version}</version> </dependency> ...
在FastJson中序列化和发序列化用的最多的两个函数就是 toJSONString
和 toJavaObject
,部分场景也会用到数据组的序列化相关函数,以及利用json进行复制对象(类似Cloneable),因此,我们需要适配基本所需的功能,以下的接口基本能涵盖大部分的场景。
public static String toJSONString(Object obj); public static String toJSONString(Object obj, Supplier<String> defaultSupplier); public static <T> T toJavaObject(String value, Class<T> tClass); public static <T> T toJavaObject(Object obj, Class<T> tClass); public static <T> T toJavaObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) public static <T> T jsonCopy(Object obj, Class<T> tClass)
数组相关的接口定义如下:
public static List<Object> toList(String value); public static List<Object> toList(Object value); public static List<Object> toList(String value, Supplier<List<Object>> defaultSuppler); public static List<Object> toList(Object value, Supplier<List<Object>> defaultSuppler); public static <T> List<T> toJavaObjectList(String value, Class<T> tClass) public static <T> List<T> toJavaObjectList(Object obj, Class<T> tClass) public static <T> List<T> toJavaObjectList(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier)
如果变量定义为JsonObject类型,可以将其修改为Map<String, Object>。为此,需定义相关的函数进行辅助变换:
public static Map<String, Object> toMap(String value); public static Map<String, Object> toMap(Object value); public static Map<String, Object> toMap(Object value, Supplier<Map<String, Object>> defaultSupplier); public static Map<String, Object> toMap(String value, Supplier<Map<String, Object>> defaultSupplier);
为了统一替换fastjson,我封装了一个Java(要求JDK8+)工具类JsonUtils,方便大家扩展。
package com.netease.is.nm.base.util; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; @Slf4j public class JsonUtils { // 加载速度太慢了,放在静态代码块中 // private static final ObjectMapper mapper = new ObjectMapper(); private static ObjectMapper mapper; /** * 设置一些通用的属性 */ static { mapper = new ObjectMapper(); // 如果json中有新增的字段并且是实体类类中不存在的,不报错 // mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); // 如果存在未知属性,则忽略不报错 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 允许key没有双引号 mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); // 允许key有单引号 mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); // 允许整数以0开头 mapper.configure(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS, true); // 允许字符串中存在回车换行控制符 mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); } public static String toJSONString(Object obj) { return obj != null ? toJSONString(obj, () -> "") : ""; } public static String toJSONString(Object obj, Supplier<String> defaultSupplier) { try { return obj != null ? mapper.writeValueAsString(obj) : defaultSupplier.get(); } catch (Throwable e) { log.error(String.format("toJSONString %s", obj != null ? obj.toString() : "null"), e); } return defaultSupplier.get(); } public static <T> T toJavaObject(String value, Class<T> tClass) { return StringUtils.isNotBlank(value) ? toJavaObject(value, tClass, () -> null) : null; } public static <T> T toJavaObject(Object obj, Class<T> tClass) { return obj != null ? toJavaObject(toJSONString(obj), tClass, () -> null) : null; } public static <T> T toJavaObject(String value, Class<T> tClass, Supplier<T> defaultSupplier) { try { if (StringUtils.isBlank(value)) { return defaultSupplier.get(); } return mapper.readValue(value, tClass); } catch (Throwable e) { log.error(String.format("toJavaObject exception: /n %s/n %s", value, tClass), e); } return defaultSupplier.get(); } public static <T> List<T> toJavaObjectList(String value, Class<T> tClass) { return StringUtils.isNotBlank(value) ? toJavaObjectList(value, tClass, () -> null) : null; } public static <T> List<T> toJavaObjectList(Object obj, Class<T> tClass) { return obj != null ? toJavaObjectList(toJSONString(obj), tClass, () -> null) : null; } public static <T> List<T> toJavaObjectList(String value, Class<T> tClass, Supplier<List<T>> defaultSupplier) { try { if (StringUtils.isBlank(value)) { return defaultSupplier.get(); } JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, tClass); return mapper.readValue(value, javaType); } catch (Throwable e) { log.error(String.format("toJavaObjectList exception /n%s/n%s", value, tClass), e); } return defaultSupplier.get(); } // 简单地直接用json复制或者转换(Cloneable) public static <T> T jsonCopy(Object obj, Class<T> tClass) { return obj != null ? toJavaObject(toJSONString(obj), tClass) : null; } public static Map<String, Object> toMap(String value) { return StringUtils.isNotBlank(value) ? toMap(value, () -> null) : null; } public static Map<String, Object> toMap(Object value) { return value != null ? toMap(value, () -> null) : null; } public static Map<String, Object> toMap(Object value, Supplier<Map<String, Object>> defaultSupplier) { if (value == null) { return defaultSupplier.get(); } try { if (value instanceof Map) { return (Map<String, Object>) value; } } catch (Exception e) { log.info("fail to convert" + toJSONString(value), e); } return toMap(toJSONString(value), defaultSupplier); } public static Map<String, Object> toMap(String value, Supplier<Map<String, Object>> defaultSupplier) { if (StringUtils.isBlank(value)) { return defaultSupplier.get(); } try { return toJavaObject(value, LinkedHashMap.class); } catch (Exception e) { log.error(String.format("toMap exception/n%s", value), e); } return defaultSupplier.get(); } public static List<Object> toList(String value) { return StringUtils.isNotBlank(value) ? toList(value, () -> null) : null; } public static List<Object> toList(Object value) { return value != null ? toList(value, () -> null) : null; } public static List<Object> toList(String value, Supplier<List<Object>> defaultSuppler) { if (StringUtils.isBlank(value)) { return defaultSuppler.get(); } try { return toJavaObject(value, List.class); } catch (Exception e) { log.error("toList exception/n" + value, e); } return defaultSuppler.get(); } public static List<Object> toList(Object value, Supplier<List<Object>> defaultSuppler) { if (value == null) { return defaultSuppler.get(); } if (value instanceof List) { return (List<Object>) value; } return toList(toJSONString(value), defaultSuppler); } }
替换Fastjson的工作量可能比较大,影响的文件数量会比较多,大家一定要多多测试。为了避免以后经常应急Fastjson安全事件和保护系统安全,这些工作还是值得的。