Jackson快速替换Fastjson之道


一· 概述

Fastjson已经连续几次爆出高危漏洞,和Structs一样,每次影响范围都比较广,殃及几乎所有的JAVA后台系统。为避免以后频繁地应急处理Fastjson的安全漏洞,痛定思痛,决定放弃Fastjson转投jackson的怀抱了。

二· 快速替换

2.1 加入依赖

在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>
            ...

2.2 适配接口

在FastJson中序列化和发序列化用的最多的两个函数就是toJSONStringtoJavaObject,部分场景也会用到数据组的序列化相关函数,以及利用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)

2.3 适配JsonObject

如果变量定义为JsonObject类型,可以将其修改为Map

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.nis.nhids2.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.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

/**
 * @Description: http://www.easysb.cn/2019/07/482.html
 * @Date: 2019/7/16
 * @Author: Jekkay Hu
 */

@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, () -> "", false) : "";
    }

    public static String toFormatJSONString(Object obj) {
        return obj != null ? toJSONString(obj, () -> "", true) : "";
    }


    public static String toJSONString(Object obj, Supplier<String> defaultSupplier, boolean format) {
        try {
            if (obj == null) {
                return defaultSupplier.get();
            }
            if (obj instanceof String) {
                return obj.toString();
            }
            if (obj instanceof Number) {
                return obj.toString();
            }
            if (format) {
                return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            }
            return mapper.writeValueAsString(obj);
        } 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);
    }

    public static long getLong(Map<String, Object> map, String key) {
        if (MapUtils.isEmpty(map)) {
            return 0L;
        }
        String valueStr = String.valueOf(map.get(key));
        if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
            return 0L;
        }
        return Long.valueOf(valueStr);
    }

    public static int getInt(Map<String, Object> map, String key) {
        if (MapUtils.isEmpty(map)) {
            return 0;
        }
        String valueStr = String.valueOf(map.get(key));
        if (StringUtils.isBlank(valueStr) || !StringUtils.isNumeric(valueStr)) {
            return 0;
        }
        return Integer.valueOf(valueStr);
    }
}

博客地址: http://www.easysb.cn/2019/07/482.html

四· 小结

替换Fastjson的工作量可能比较大,影响的文件数量会比较多,大家一定要多多测试。为了避免以后经常应急Fastjson安全事件和保护系统安全,这些工作还是值得的。

GitHub 加速计划 / fastj / fastjson
25.69 K
6.51 K
下载
FASTJSON 2.0.x has been released, faster and more secure, recommend you upgrade.
最近提交(Master分支:3 个月前 )
c942c834 - 1 年前
5bc4709b - 1 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐