fastjson: чуть не испортил себе репутацию из-за нескольких багов

Java задняя часть
fastjson: чуть не испортил себе репутацию из-за нескольких багов

01. Прошлая и настоящая жизнь

Я fastjson, уроженец Ханчжоу, но у меня всегда есть стремление выйти на глобальный уровень. Нет, мой профиль на GitHub изменился на английский, это интернациональный стиль?

Если ваши знания английского языка не так хороши, как у моего босса 666, я могу просто перевести (говорите человеческими словами, не притворяйтесь).

Я являюсь библиотекой синтаксического анализа JSON Alibaba, которая может сериализовать объекты Java в строки JSON, а также может упорядочивать строку JSON для объектов Java.

  • Я предоставил два инструмента для синтаксического анализа на стороне сервера и на стороне клиента Android, и производительность неплохая.

  • Я предоставляю удобный способ преобразования между объектами Java и JSON,toJSONString()метод используется для сериализации,parseObject()способ десериализации.

  • Я разрешаю преобразование уже существующих неизменяемых объектов (только классы, без исходного кода).

  • Существует обширная поддержка дженериков Java.

  • Поддерживаю любой сложный объект (глубина иерархии наследования).

В 2012 году меня открыли в Китай, чтобы выбрать одно из самых популярных отечественных программ с открытым исходным кодом. Спустя годы мой эпидемический тренд не потерял, в области JSON смею сказать, что я NO 1, потому что количество моих поклонников на Github перевалило за 22К, никто не смеет игнорировать мои достижения.

02. Руководство пользователя

Прежде чем использовать мой API, мне нужно импортировать свои зависимости в файл pom.xml.

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.58</version>
</dependency>

Позвольте мне написать простой тестовый пример, вы можете взглянуть.

public class Test {
    public static void main(String[] args) {
        Writer writer = new Writer();
        writer.setAge(18);
        writer.setName("沉默王二");

        String json = JSON.toJSONString(writer);
        System.out.println(json);
    }
}
class Writer {
    private int age;
    private String name;

    // getter/setter
}

Writer — это обычный класс Java с двумя полями: age и name, и соответствующими им методами получения и установки.

main()метод создает объект Writer, а затем вызывает статический метод, который я предоставляюJSON.toJSONString()чтобы получить строку JSON.

Посмотрите на распечатанный результат.

{"age":18,"name":"沉默王二"}

Если вы хотите десериализовать, выполните следующий код.

Writer writer1 = JSON.parseObject(json, Writer.class);

вызов статического методаJSON.parseObject(), передавая два параметра, один из которых представляет собой строку JSON, а другой — тип объекта.

Если вы хотите преобразовать строку JSON в коллекцию, вам нужно вызвать другой статический метод.JSON.parseArray().

List<Writer> list = JSON.parseArray("[{\"age\":18,\"name\":\"沉默王二\"},{\"age\":19,\"name\":\"沉默王一\"}]", Writer.class);

Если нет особых требований, смею сказать, что вышеперечисленные три метода могут покрыть большинство ваших бизнес-сценариев.

03. Используйте аннотации

Иногда ключ в вашей строке JSON может не совпадать с полем в объекте Java, например case; иногда вам нужно указать, что некоторые поля сериализуются, но не десериализуются; иногда вам нужно, чтобы поле даты отображалось в указанном формате.

Я рассмотрел все эти специальные сценарии для вас, просто добавьте соответствующие поля@JSONFieldАннотации достаточно.

Давайте взглянем@JSONFieldОпределение аннотаций.

public @interface JSONField {
    String name() default "";
    String format() default "";
    boolean serialize() default true;
    boolean deserialize() default true;
}

имя используется для указания имени поля, формат используется для указания формата даты, сериализация и десериализация используются для указания необходимости сериализации и десериализации.

class Writer {
    private int age;
    private String name;
    private Date birthday;

    @JSONField(format = "yyyy年MM月dd日")
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @JSONField(name = "Age")
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @JSONField(serialize = false,deserialize = true)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Я рекомендую использовать в полях геттера@JSONFieldаннотация. Давайте посмотрим на тестовый код.

Writer writer = new Writer();
writer.setAge(18);
writer.setName("沉默王二");
writer.setBirthday(new Date());

String json = JSON.toJSONString(writer);
System.out.println(json);

Выход на данный момент следующий.

{"Age":18,"birthday":"2020年12月17日"}

Первая буква возраста в строке JSON написана заглавной, формат дня рождения соответствует ожиданию «год месяц день», а поле имени не отображается в результате, что указывает на то, что оно не было сериализовано.

04. Функции сериализации

Чтобы удовлетворить более персонализированные потребности, я определяю множество функций в классе SerializerFeature, вы можете вызватьtoJSONString()указан метод.

  • PrettyFormat, сделайте печать в формате JSON более красивой
  • WriteClassName, имя выходного класса
  • UseSingleQuotes, ключ использует одинарные кавычки
  • WriteNullListAsEmpty, вывод [], если список пуст
  • WriteNullStringAsEmpty, вывод «», если строка пуста

Подождите, подождите, новые навыки ждут, пока вы разблокируете их. Я пишу простую демонстрацию здесь для вашей справки.

System.out.println(JSON.toJSONString(writer, 
SerializerFeature.PrettyFormat, 
SerializerFeature.UseSingleQuotes));

Сравните результаты до и после настройки.

{"Age":18,"birthday":"2020年12月17日"}
{
	'Age':18,
	'birthday':'2020年12月17日'
}

05. Почему я быстрый

Как мы все знаем, для сериализации объектов Java в строки JSON невозможно использовать строки для прямой конкатенации, потому что производительность очень низкая. Лучше, чем конкатенация строк, использоватьStringBuilder.

StringBuilder хорош, но производительность можно улучшить. «Сделай сам, хватит еды и одежды», поэтому я создал класс SerializeWriter, посвященный сериализации.

Класс SerializeWriter содержитchar[] buf, выделение требуется для каждой сериализации, но я использую ThreadLocal для оптимизации, что позволяет эффективно сократить выделение объектов и сборку мусора, тем самым повышая производительность.

private final static ThreadLocal<char[]> bufLocal         = new ThreadLocal<char[]>();

public SerializeWriter(java.io.Writer writer, int defaultFeatures, SerializerFeature... features){
    this.writer = writer;

    buf = bufLocal.get();

    if (buf != null) {
        bufLocal.set(null);
    } else {
        buf = new char[2048];
    }
}

В дополнение к этому, есть много других деталей, таких как использование идентификатораhashmap вместо Hashmap, которые могут избежать избыточногоequalsоперации и может избежать бесконечного цикла в случае многопоточного параллелизма.

/**
 * for concurrent IdentityHashMap
 * 
 * @author wenshao[szujobs@hotmail.com]
 */
@SuppressWarnings("unchecked")
public class IdentityHashMap<K, V> {
    private final Entry<K, V>[] buckets;
    private final int           indexMask;
    public final static int DEFAULT_SIZE = 8192;
}

Другим примером является использование технологии asm, чтобы избежать накладных расходов, вызванных отражением.

Я признаю, что, хотя это быстро, это также приносит некоторые проблемы с безопасностью. Особенно введение AutoType дает хакерам возможность.

Выпущена версия 1.2.59, повышена безопасность при включенном AutoType.

Выпущена версия 1.2.60, добавлен черный список AutoType, исправлены проблемы безопасности с отказом в обслуживании.

Выпущена версия 1.2.61 с добавлением черного списка безопасности AutoType.

Выпущена версия 1.2.62 с добавлением черного списка AutoType, улучшенной десериализацией даты и JSONPath.

Выпущена версия 1.2.66, исправления ошибок и усиление безопасности, а также усиление безопасности, добавлен черный список AutoType.

Выпущена версия 1.2.67, исправлены ошибки и усилена безопасность, добавлен черный список AutoType.

Выпущена версия 1.2.68, поддерживающая GEOJSON и дополняющая черный список AutoType. (Представлена ​​конфигурация safeMode. После настройки safeMode автотип не поддерживается независимо от белого или черного списка.)

Выпущена версия 1.2.69, исправлена ​​недавно обнаруженная уязвимость безопасности, связанная с обходом переключателя AutoType с высоким риском, и дополнен черный список AutoType.

Выпущена версия 1.2.70, улучшена совместимость, добавлен черный список AutoType

В неоднократных битвах хакеров, хотя я становился все более и более стабильным и зрелым, в то же время мои пользователи также заплатили за это высокую цену.

Сеть там было много неуправляющихся голосов, они утверждали, что я был одним из самых мусорных программных программ, но с некоторым оппортунистическим завоевал доверие домашних разработчиков.

Но более того, он никогда не отказывался от меня.

Фраза, которая меня больше всего тронула:

Вэнь Шао создал широко используемую библиотеку JSON почти в одиночку, и почти все другие библиотеки полагаются на целую команду.Основываясь на этом, Вэнь Шао заслуженно считается «первым поколением людей с открытым исходным кодом в Али, которые никогда не меняли свой первоначальный намерение."

Возникновение лазеек не страшно, страшно то, что лазейки не могут быть найдены, или лазейки не могут быть раскрыты.

Чтобы полностью решить проблемы, вызванные AutoType, в версии 1.2.68 я ввел безопасный режим safeMode, независимо от белого или черного списка, AutoType не поддерживается, так что атаки могут быть полностью исключены.

В безопасном режимеcheckAutoType()Метод напрямую вызывает исключение.

06. Эпилог

Сколько бы трудностей и невзгод ни было на пути впереди, и сколько бы слухов мне ни пришлось столкнуться, я буду идти вперед.Для энергичного развития отечественного открытого программного обеспечения я готов быть первооткрывателем и долгостроем. истребитель.

Последняя статья в 2020 году, давайте подбодримся в 2021 году, поставим лайк и сохраним этот последний момент.

Рекомендуемое чтение:

Трехстрочный программист в 2020 году, прилично | ежегодное эссе народ

Потрясающие, более 115 000 руководств по Java на GitHub!