From b4363e1572da2d0dd4b065eaec2a25412064ecee Mon Sep 17 00:00:00 2001 From: Les Hazlewood <121180+lhazlewood@users.noreply.github.com> Date: Wed, 5 Feb 2020 12:08:18 -0800 Subject: [PATCH] - Update README to use `parserBuilder()` instead of `parser()` (#499) (#559) - Docs: Adds section to README covering custom object parsing (#500) - Docs: Add note about JwtParserBuilder creating an immutable JwtParser (#508) Doc: #486 Fixes: #494 Doc: #495 Fixes: #171 Updated documentation and changelog to reflect the new Gson extension. Fixes #410. (#476) Co-authored-by: Brian Demers --- CHANGELOG.md | 15 +- README.md | 236 ++++++++++++++---- .../io/jsonwebtoken/JwtParserBuilder.java | 4 +- api/src/main/java/io/jsonwebtoken/Jwts.java | 4 +- 4 files changed, 199 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d7a7ae1..aa4896ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ This minor release: +* Adds [Google's Gson](https://github.com/google/gson) as a natively supported JSON parser. Installation instructions + have been updated and new [JJWT Gson usage guidelines](https://github.com/jwtk/jjwt#json-gson) have been added. * Updates the Jackson dependency version to [2.9.10](https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9#patches) to address three security vulnerabilities in Jackson. -* A new JwtParserBuilder interface has been added and is the recommended way of creating a JwtParser instance. Mutable methods in `JwtParser` will be removed before v1.0. +* A new `JwtParserBuilder` interface has been added and is the recommended way of creating an immutable and thread-safe JwtParser instance. Mutable methods in `JwtParser` will be removed before v1.0. Migration to the new signatures is straightforward, for example: Previous Version: @@ -22,8 +24,15 @@ to address three security vulnerabilities in Jackson. .build() .parse(jwtString) ``` -* Adds support for custom types when deserializing with Jackson. To use configure your parser with `Jwts.parserBuilder().deserializeJsonWith(new JacksonDeserializer(Maps.of("claimName", YourType.class).build())).build()`. -* Adds `io.jsonwebtoken.lang.Maps` utility class to make creation of maps fluent. +* Adds `io.jsonwebtoken.lang.Maps` utility class to make creation of maps fluent, as demonstrated next. +* Adds support for custom types when deserializing with Jackson. To use configure your parser: + ```java + Jwts.parserBuilder().deserializeJsonWith( + new JacksonDeserializer( + Maps.of("claimName", YourType.class).build() // <-- + ) + ).build() + ``` * Moves JSON Serializer/Deserializer implementations to a different package name. - `io.jsonwebtoken.io.JacksonSerializer` -> `io.jsonwebtoken.jackson.io.JacksonSerializer` - `io.jsonwebtoken.io.JacksonDeserializer` -> `io.jsonwebtoken.jackson.io.JacksonDeserializer` diff --git a/README.md b/README.md index 53172e19..d0d0f358 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,8 @@ enforcement. * [JSON Processor](#json) * [Custom JSON Processor](#json-custom) * [Jackson ObjectMapper](#json-jackson) + * [Custom Claim Types](#json-jackson-custom-types) + * [Gson](#json-gson) * [Base64 Support](#base64) * [Base64 in Security Contexts](#base64-security) * [Base64 is not Encryption](#base64-not-encryption) @@ -264,21 +266,22 @@ If you're building a (non-Android) JDK project, you will want to define the foll io.jsonwebtoken jjwt-api - 0.10.7 + 0.11.0-SNAPSHOT io.jsonwebtoken jjwt-impl - 0.10.7 + 0.11.0-SNAPSHOT runtime io.jsonwebtoken - jjwt-jackson - 0.10.7 + jjwt-jackson + 0.11.0-SNAPSHOT runtime - ``` @@ -1318,7 +1339,7 @@ scope which is the typical JJWT default). That is: ```groovy dependencies { - compile 'io.jsonwebtoken:jjwt-jackson:0.10.7' + compile 'io.jsonwebtoken:jjwt-jackson:0.11.0-SNAPSHOT' } ``` @@ -1334,18 +1355,127 @@ String jws = Jwts.builder() // ... etc ... ``` -and the `JacksonDeserializer` using your `ObjectMapper` on the `JwtParser`: +and the `JacksonDeserializer` using your `ObjectMapper` on the `JwtParserBuilder`: ```java ObjectMapper objectMapper = getMyObjectMapper(); //implement me -Jwts.parser() +Jwts.parserBuilder() .deserializeJsonWith(new JacksonDeserializer(objectMapper)) // ... etc ... ``` + +#### Parsing of Custom Claim Types + +By default JJWT will only convert simple claim types: String, Date, Long, Integer, Short and Byte. If you need to deserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in through a constructor. For example: + +```java +new JacksonDeserializer(Maps.of("user", User.class).build()) +``` + +This would trigger the value in the `user` claim to be deserialized into the custom type of `User`. Given the claims body of: + +```json +{ + "issuer": "https://example.com/issuer", + "user": { + "firstName": "Jill", + "lastName": "Coder" + } +} +``` + +The `User` object could be retrieved from the `user` claim with the following code: + +```java +Jwts.parserBuilder() + + .deserializeJsonWith(new JacksonDeserializer(Maps.of("user", User.class).build())) // <----- + + .build() + + .parseClaimsJwt(aJwtString) + + .getBody() + + .get("user", User.class) // <----- +``` + +**NOTE:** Using this constructor is mutually exclusive with the `JacksonDeserializer(ObjectMapper)` constructor +[described above](#json-jackson). This is because JJWT configures an `ObjectMapper` directly and could have negative +consequences for a shared `ObjectMapper` instance. This should work for most applications, if you need a more advanced +parsing options, [configure the mapper directly](#json-jackson). + + +### Gson JSON Processor + +If you want to use Gson for JSON processing, just including the `io.jsonwebtoken:jjwt-gson` dependency as a +runtime dependency is all that is necessary in most projects, since Gradle and Maven will automatically pull in +the necessary Gson dependencies as well. + +After including this dependency, JJWT will automatically find the Gson implementation on the runtime classpath and +use it internally for JSON parsing. There is nothing else you need to do - just declaring the dependency is +all that is required, no code or config is necessary. + +If you're curious, JJWT will automatically create an internal default Gson instance for its own needs as follows: + +```java +new GsonBuilder().disableHtmlEscaping().create(); +``` + +However, if you prefer to use a different Gson instance instead of JJWT's default, you can configure JJWT to use your +own. + +You do this by declaring the `io.jsonwebtoken:jjwt-gson` dependency with **compile** scope (not runtime +scope which is the typical JJWT default). That is: + +**Maven** + +```xml + + io.jsonwebtoken + jjwt-gson + 0.11.0-SNAPSHOT + compile + +``` + +**Gradle or Android** + +```groovy +dependencies { + compile 'io.jsonwebtoken:jjwt-gson:0.11.0-SNAPSHOT' +} +``` + +And then you can specify the `GsonSerializer` using your own `Gson` instance on the `JwtBuilder`: + +```java + +Gson gson = getGson(); //implement me + +String jws = Jwts.builder() + + .serializeToJsonWith(new GsonSerializer(gson)) + + // ... etc ... +``` + +and the `GsonDeserializer` using your `Gson` instance on the `JwtParser`: + +```java +Gson gson = getGson(); //implement me + +Jwts.parser() + + .deserializeJsonWith(new GsonDeserializer(gson)) + + // ... etc ... +``` + ## Base64 Support @@ -1481,12 +1611,12 @@ String jws = Jwts.builder() // ... etc ... ``` -and the `JwtParser`'s `base64UrlDecodeWith` method to set the decoder: +and the `JwtParserBuilder`'s `base64UrlDecodeWith` method to set the decoder: ```java Decoder base64UrlDecoder = getMyBase64UrlDecoder(); //implement me -Jwts.parser() +Jwts.parserBuilder() .base64UrlDecodeWith(base64UrlEncoder) diff --git a/api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java b/api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java index 0c8e06cd..6dc24c8d 100644 --- a/api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java +++ b/api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java @@ -298,8 +298,8 @@ public interface JwtParserBuilder { JwtParserBuilder deserializeJsonWith(Deserializer> deserializer); /** - * Returns a {@link JwtParser} created from the configuration from this JwtParserBuilder. - * @return a JwtParser created from the configuration from this JwtParserBuilder. + * Returns an immutable/thread-safe {@link JwtParser} created from the configuration from this JwtParserBuilder. + * @return an immutable/thread-safe JwtParser created from the configuration from this JwtParserBuilder. */ JwtParser build(); } diff --git a/api/src/main/java/io/jsonwebtoken/Jwts.java b/api/src/main/java/io/jsonwebtoken/Jwts.java index 255483e9..80de65a4 100644 --- a/api/src/main/java/io/jsonwebtoken/Jwts.java +++ b/api/src/main/java/io/jsonwebtoken/Jwts.java @@ -122,9 +122,9 @@ public final class Jwts { } /** - * Returns a new {@link JwtParserBuilder} instance that can be configured and then used to parse JWT strings. + * Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser). * - * @return a new {@link JwtParser} instance that can be configured and then used to parse JWT strings. + * @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser). */ public static JwtParserBuilder parserBuilder() { return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtParserBuilder");