Updates Jackson usage to use immutable ObjectReader/Writer instead of ObjectMapper

Jackson 2.10+ recommend using `ObjectReader` and `ObjectWriter` as opposed to `ObjectMapper`
https://cowtowncoder.medium.com/jackson-3-0-immutability-w-builders-d9c532860d88
This commit is contained in:
Brian Demers 2021-07-12 15:45:30 -04:00
parent 9007ae7c98
commit 0c55bfce67
No known key found for this signature in database
GPG Key ID: 78A3F0D70AE5308C
6 changed files with 29 additions and 21 deletions

View File

@ -5,7 +5,8 @@
This patch release:
* Adds additional handling for rare JSON parsing exceptions and wraps them in a `JwtException` to allow the application to handle these conditions as JWT concerns.
* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.9.10.7`.
* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.4`.
* Updates Jackson usage (in `jjwt-jackson`) to use immutable classes instead of using `ObjectMapper` directly.
### 0.11.2

View File

@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import io.jsonwebtoken.io.DeserializationException;
@ -35,7 +36,7 @@ import java.util.Map;
public class JacksonDeserializer<T> implements Deserializer<T> {
private final Class<T> returnType;
private final ObjectMapper objectMapper;
private final ObjectReader objectReader;
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
public JacksonDeserializer() {
@ -68,14 +69,7 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
* @param claimTypeMap The claim name-to-class map used to deserialize claims into the given type
*/
public JacksonDeserializer(Map<String, Class> claimTypeMap) {
// DO NOT reuse JacksonSerializer.DEFAULT_OBJECT_MAPPER as this could result in sharing the custom deserializer
// between instances
this(new ObjectMapper());
Assert.notNull(claimTypeMap, "Claim type map cannot be null.");
// register a new Deserializer
SimpleModule module = new SimpleModule();
module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap)));
objectMapper.registerModule(module);
this(objectMapperWithMappedTypes(claimTypeMap));
}
@SuppressWarnings({"unchecked", "WeakerAccess", "unused"}) // for end-users providing a custom ObjectMapper
@ -86,7 +80,7 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
private JacksonDeserializer(ObjectMapper objectMapper, Class<T> returnType) {
Assert.notNull(objectMapper, "ObjectMapper cannot be null.");
Assert.notNull(returnType, "Return type cannot be null.");
this.objectMapper = objectMapper;
this.objectReader = objectMapper.reader();
this.returnType = returnType;
}
@ -101,7 +95,19 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
}
protected T readValue(byte[] bytes) throws IOException {
return objectMapper.readValue(bytes, returnType);
return objectReader.readValue(bytes, returnType);
}
private static ObjectMapper objectMapperWithMappedTypes(Map<String, Class> claimTypeMap) {
// DO NOT reuse JacksonSerializer.DEFAULT_OBJECT_MAPPER as this could result in sharing the custom deserializer
// between instances
Assert.notNull(claimTypeMap, "Claim type map cannot be null.");
ObjectMapper objectMapper = new ObjectMapper();
// register a new Deserializer
SimpleModule module = new SimpleModule();
module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap)));
objectMapper.registerModule(module);
return objectMapper;
}
/**
@ -122,7 +128,7 @@ public class JacksonDeserializer<T> implements Deserializer<T> {
// check if the current claim key is mapped, if so traverse it's value
String name = parser.currentName();
if (claimTypeMap != null && name != null && claimTypeMap.containsKey(name)) {
Class type = claimTypeMap.get(name);
Class<?> type = claimTypeMap.get(name);
return parser.readValueAsTree().traverse(parser.getCodec()).readValueAs(type);
}
// otherwise default to super

View File

@ -17,6 +17,7 @@ package io.jsonwebtoken.jackson.io;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import io.jsonwebtoken.io.SerializationException;
import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.lang.Assert;
@ -28,7 +29,7 @@ public class JacksonSerializer<T> implements Serializer<T> {
static final ObjectMapper DEFAULT_OBJECT_MAPPER = new ObjectMapper();
private final ObjectMapper objectMapper;
private final ObjectWriter objectWriter;
@SuppressWarnings("unused") //used via reflection by RuntimeClasspathDeserializerLocator
public JacksonSerializer() {
@ -38,7 +39,7 @@ public class JacksonSerializer<T> implements Serializer<T> {
@SuppressWarnings("WeakerAccess") //intended for end-users to use when providing a custom ObjectMapper
public JacksonSerializer(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "ObjectMapper cannot be null.");
this.objectMapper = objectMapper;
this.objectWriter = objectMapper.writer();
}
@Override
@ -54,6 +55,6 @@ public class JacksonSerializer<T> implements Serializer<T> {
@SuppressWarnings("WeakerAccess") //for testing
protected byte[] writeValueAsBytes(T t) throws JsonProcessingException {
return this.objectMapper.writeValueAsBytes(t);
return this.objectWriter.writeValueAsBytes(t);
}
}

View File

@ -38,14 +38,14 @@ class JacksonDeserializerTest {
@Test
void testDefaultConstructor() {
def deserializer = new JacksonDeserializer()
assertNotNull deserializer.objectMapper
assertNotNull deserializer.objectReader
}
@Test
void testObjectMapperConstructor() {
def customOM = new ObjectMapper()
def deserializer = new JacksonDeserializer(customOM)
assertSame customOM, deserializer.objectMapper
assertSame customOM.getDeserializationConfig(), deserializer.objectReader.config
}
@Test(expected = IllegalArgumentException)

View File

@ -37,14 +37,14 @@ class JacksonSerializerTest {
@Test
void testDefaultConstructor() {
def serializer = new JacksonSerializer()
assertNotNull serializer.objectMapper
assertNotNull serializer.objectWriter
}
@Test
void testObjectMapperConstructor() {
def customOM = new ObjectMapper()
def serializer = new JacksonSerializer<>(customOM)
assertSame customOM, serializer.objectMapper
assertSame customOM.getSerializationConfig(), serializer.objectWriter.config
}
@Test(expected = IllegalArgumentException)

View File

@ -74,7 +74,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<buildNumber>${user.name}-${maven.build.timestamp}</buildNumber>
<jackson.version>2.9.10.7</jackson.version>
<jackson.version>2.12.4</jackson.version>
<orgjson.version>20180130</orgjson.version>
<gson.version>2.8.5</gson.version>