JJWT / Джексон изменяют используемый ObjectMapper

Для проекта, над которым я работаю, я должен использовать Spring Security и JSON Webtokens, как это предусмотрено библиотекой io.jsonwebtoken (jjwt). Одно из требований, которое я должен добавить к сгенерированному токену, - это следующая сущность (упрощенная для иллюстрации проблемы):

@Entity
public class MyEntity {
    private String name;
    private LocalDateTime ldt;
}

Это работает, но полученный веб-токен сериализуется следующим образом:

{
  "sub": "[email protected]",
  "exp": 1523659655,
  "entity": {
    "name": testname,
    "ldt": {
      "hour": 0,
      "minute": 37,
      "dayOfMonth": 12,
      "dayOfWeek": "THURSDAY",
      "dayOfYear": 102,
      "year": 2018,
      "month": "APRIL",
      "monthValue": 4,
      "second": 38,
      "nano": 569000000,
      "chronology": {
        "calendarType": "iso8601",
        "id": "ISO"
      }
    }
  }
}

Это может показаться не проблемой, но на самом деле это проблема, когда позже мне потребуется снова сопоставить ее с экземпляром MyEntity. После некоторого чтения в Интернете я решил, что мне нужно изменить конфигурацию ObjectMapper, чтобы изменить параметры конфигурации (переключить флаг WRITE_DATES_AS_TIMESTAMPS на false). Однако я не могу изменить конфигурацию ObjectMapper, используемого jjwt, поскольку он построен следующим образом (источник jjwt):

public class DefaultJwtBuilder implements JwtBuilder {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    ...
}

Другой вариант, который я нашел в Интернете, заключался в том, чтобы поместить следующую строку в мой файл application.properties:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false

Но безрезультатно, ObjectMapper, используемый jjwt, похоже, игнорирует эти свойства.

Что бы я сделал для достижения своей цели?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
1 740
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я исправил это, переопределив DefaultJwtBuilder, например:

public class CustomJwtBuilder extends DefaultJwtBuilder {
    private static final ObjectMapper mapper = new Jackson2ObjectMapperBuilder()
        .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        .modulesToInstall(new JavaTimeModule())
        .build();

    @Override
    protected byte[] toJson(Object object) throws JsonProcessingException {
        return mapper.writeValueAsBytes(object);
    }
}

А затем создав такой токен

String token = new CustomJwtBuilder()
            .setSubject(..)
            .setExpiration(...)
            .compact();

Я понимаю, что этот ответ был предоставлен до того, как был выпущен JJWT 0.10.0, но после версии 0.10.0 рекомендуется использовать ответ tete на этой странице. Пользователи JJWT должны следовать этому. Ваше здоровье!

Les Hazlewood 07.11.2019 18:39

@LesHazlewood Я принял этот ответ; когда я изначально задал вопрос, это было невозможно.

Pieter De Clercq 07.11.2019 22:59

Я знаю, я сказал: «Я так понимаю, что этот ответ был дан до того, как был выпущен JJWT 0.10.0» :). Но спасибо за обновление ответа! ;)

Les Hazlewood 08.11.2019 20:57
Ответ принят как подходящий

В документации Jjwt указано, что (начиная с версии 0.10.0 и далее) вы можете внедрить свой собственный ObjectMapper с определенной зависимостью компиляции и реализовать инъекцию следующим образом:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>compile</scope> <!-- Not runtime -->
</dependency>

... затем добавьте маппер в JwtBuilder:

ObjectMapper objectMapper = getMyObjectMapper(); //implement me
String jws = Jwts.builder()
    .serializeToJsonWith(new JacksonSerializer(objectMapper))
    // ... etc ...

как задокументировано здесь

Для тех, кто задается вопросом, как реализовать getMyObjectMapper(), это зависит от того, как вы управляете внедрением зависимостей. Если вы используете простую старую Java, подойдет return new ObjectMapper(); (с желаемой конфигурацией); если вы используете Spring, возможно, этот метод возвращает экземпляр с автоматическим подключением. Это выходит за рамки этого вопроса.

tete 04.03.2020 15:38

Обычно это происходит, когда компилятор не может обнаружить какие-либо реализации сериализатора JSON в пути к классам. Включите следующую зависимость jjwt jackson и перестройте проект. Это решило мою проблему.

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>compile</scope>
</dependency>

Другие вопросы по теме