- 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 <brian.demers@gmail.com>
This commit is contained in:
Les Hazlewood 2020-02-05 12:08:18 -08:00 committed by GitHub
parent 1aa8225703
commit b4363e1572
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 60 deletions

View File

@ -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`

236
README.md
View File

@ -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
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.7</version>
<version>0.11.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.7</version>
<version>0.11.0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
<!-- Uncomment this next dependency if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
<!-- Uncomment this next dependency if you are using JDK 10 or earlier and you also want to use
RSASSA-PSS (PS256, PS384, PS512) algorithms. JDK 11 or later does not require it for those algorithms:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
@ -294,11 +297,11 @@ If you're building a (non-Android) JDK project, you will want to define the foll
```groovy
dependencies {
compile 'io.jsonwebtoken:jjwt-api:0.10.7'
runtime 'io.jsonwebtoken:jjwt-impl:0.10.7',
compile 'io.jsonwebtoken:jjwt-api:0.11.0-SNAPSHOT'
runtime 'io.jsonwebtoken:jjwt-impl:0.11.0-SNAPSHOT',
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
//'org.bouncycastle:bcprov-jdk15on:1.60',
'io.jsonwebtoken:jjwt-jackson:0.10.7'
'io.jsonwebtoken:jjwt-jackson:0.11.0-SNAPSHOT' // or 'io.jsonwebtoken:jjwt-gson:0.11.0-SNAPSHOT' for gson
}
```
@ -314,9 +317,9 @@ Add the dependencies to your project:
```groovy
dependencies {
api 'io.jsonwebtoken:jjwt-api:0.10.7'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.10.7'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.10.7') {
api 'io.jsonwebtoken:jjwt-api:0.11.0-SNAPSHOT'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.0-SNAPSHOT'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.0-SNAPSHOT') {
exclude group: 'org.json', module: 'json' //provided by Android natively
}
// Uncomment the next line if you want to use RSASSA-PSS (PS256, PS384, PS512) algorithms:
@ -398,7 +401,7 @@ eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60A
Now let's verify the JWT (you should always discard JWTs that don't match an expected signature):
```java
assert Jwts.parser().setSigningKey(key).parseClaimsJws(jws).getBody().getSubject().equals("Joe");
assert Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jws).getBody().getSubject().equals("Joe");
```
**NOTE: Ensure you call the `parseClaimsJws` method** (since there are many similar methods available). You will get an `UnsupportedJwtException` if you parse your JWT with wrong method.
@ -414,7 +417,7 @@ But what if parsing or signature validation failed? You can catch `JwtException
```java
try {
Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(compactJws);
//OK, we can trust this JWT
@ -930,10 +933,11 @@ Please see the main [Compression](#compression) section to see how to compress a
You read (parse) a JWS as follows:
1. Use the `Jwts.parser()` method to create a `JwtParser` instance.
1. Use the `Jwts.parserBuilder()` method to create a `JwtParserBuilder` instance.
2. Specify the `SecretKey` or asymmetric `PublicKey` you want to use to verify the JWS signature.<sup>1</sup>
3. Finally, call the `parseClaimsJws(String)` method with your jws `String`, producing the original JWS.
4. The entire call is wrapped in a try/catch block in case parsing or signature validation fails. We'll cover
3. Call the `build()` method on the `JwtParserBuilder` to return a thread-safe `JwtParser`.
4. Finally, call the `parseClaimsJws(String)` method with your jws `String`, producing the original JWS.
5. The entire call is wrapped in a try/catch block in case parsing or signature validation fails. We'll cover
exceptions and causes for failure later.
<sup>1. If you don't know which key to use at the time of parsing, you can look up the key using a `SigningKeyResolver`
@ -945,13 +949,14 @@ For example:
Jws<Claims> jws;
try {
jws = Jwts.parser() // (1)
jws = Jwts.parserBuilder() // (1)
.setSigningKey(key) // (2)
.parseClaimsJws(jwsString); // (3)
.build() // (3)
.parseClaimsJws(jwsString); // (4)
// we can safely trust the JWT
} catch (JwtException ex) { // (4)
catch (JwtException ex) { // (5)
// we *cannot* use the JWT as intended by its creator
}
@ -969,23 +974,25 @@ discarded.
So which key do we use for verification?
* If the jws was signed with a `SecretKey`, the same `SecretKey` should be specified on the `JwtParser`. For example:
* If the jws was signed with a `SecretKey`, the same `SecretKey` should be specified on the `JwtParserBuilder`. For example:
```java
Jwts.parser()
Jwts.parserBuilder()
.setSigningKey(secretKey) // <----
.build()
.parseClaimsJws(jwsString);
```
* If the jws was signed with a `PrivateKey`, that key's corresponding `PublicKey` (not the `PrivateKey`) should be
specified on the `JwtParser`. For example:
specified on the `JwtParserBuilder`. For example:
```java
Jwts.parser()
Jwts.parserBuilder()
.setSigningKey(publicKey) // <---- publicKey, not privateKey
.build()
.parseClaimsJws(jwsString);
```
@ -993,7 +1000,7 @@ But you might have noticed something - what if your application doesn't use just
if JWSs can be created with different `SecretKey`s or public/private keys, or a combination of both? How do you
know which key to specify if you can't inspect the JWT first?
In these cases, you can't call the `JwtParser`'s `setSigningKey` method with a single key - instead, you'll need
In these cases, you can't call the `JwtParserBuilder`'s `setSigningKey` method with a single key - instead, you'll need
to use a `SigningKeyResolver`, covered next.
<a name="jws-read-key-resolver"></a>
@ -1001,16 +1008,17 @@ to use a `SigningKeyResolver`, covered next.
If your application expects JWSs that can be signed with different keys, you won't call the `setSigningKey` method.
Instead, you'll need to implement the
`SigningKeyResolver` interface and specify an instance on the `JwtParser` via the `setSigningKeyResolver` method.
`SigningKeyResolver` interface and specify an instance on the `JwtParserBuilder` via the `setSigningKeyResolver` method.
For example:
```java
SigningKeyResolver signingKeyResolver = getMySigningKeyResolver();
Jwts.parser()
Jwts.parserBuilder()
.setSigningKeyResolver(signingKeyResolver) // <----
.build()
.parseClaimsJws(jwsString);
```
@ -1087,11 +1095,11 @@ application.
For example, let's say that you require that the JWS you are parsing has a specific `sub` (subject) value,
otherwise you may not trust the token. You can do that by using one of the various `require`* methods on the
`JwtParser`:
`JwtParserBuilder`:
```java
try {
Jwts.parser().requireSubject("jsmith").setSigningKey(key).parseClaimsJws(s);
Jwts.parserBuilder().requireSubject("jsmith").setSigningKey(key).build().parseClaimsJws(s);
} catch(InvalidClaimException ice) {
// the sub field was missing or did not have a 'jsmith' value
}
@ -1102,7 +1110,7 @@ you can catch either `MissingClaimException` or `IncorrectClaimException`:
```java
try {
Jwts.parser().requireSubject("jsmith").setSigningKey(key).parseClaimsJws(s);
Jwts.parserBuilder().requireSubject("jsmith").setSigningKey(key).build().parseClaimsJws(s);
} catch(MissingClaimException mce) {
// the parsed JWT did not have the sub field
} catch(IncorrectClaimException ice) {
@ -1114,14 +1122,14 @@ You can also require custom fields by using the `require(fieldName, requiredFiel
```java
try {
Jwts.parser().require("myfield", "myRequiredValue").setSigningKey(key).parseClaimsJws(s);
Jwts.parserBuilder().require("myfield", "myRequiredValue").setSigningKey(key).build().parseClaimsJws(s);
} catch(InvalidClaimException ice) {
// the 'myfield' field was missing or did not have a 'myRequiredValue' value
}
```
(or, again, you could catch either `MissingClaimException` or `IncorrectClaimException` instead).
Please see the `JwtParser` class and/or JavaDoc for a full list of the various `require`* methods you may use for claims
Please see the `JwtParserBuilder` class and/or JavaDoc for a full list of the various `require`* methods you may use for claims
assertions.
<a name="jws-read-clock"></a>
@ -1132,17 +1140,18 @@ the parsing machine is not perfectly in sync with the clock on the machine that
obvious problems since `exp` and `nbf` are time-based assertions, and clock times need to be reliably in sync for shared
assertions.
You can account for these differences (usually no more than a few minutes) when parsing using the `JwtParser`'s
You can account for these differences (usually no more than a few minutes) when parsing using the `JwtParserBuilder`'s
`setAllowedClockSkewSeconds`. For example:
```java
long seconds = 3 * 60; //3 minutes
Jwts.parser()
Jwts.parserBuilder()
.setAllowedClockSkewSeconds(seconds) // <----
// ... etc ...
.build()
.parseClaimsJws(jwt);
```
This ensures that clock differences between the machines can be ignored. Two or three minutes should be more than
@ -1153,13 +1162,13 @@ atomic clocks around the world.
##### Custom Clock Support
If the above `setAllowedClockSkewSeconds` isn't sufficient for your needs, the timestamps created
during parsing for timestamp comparisons can be obtained via a custom time source. Call the `JwtParser`'s `setClock`
during parsing for timestamp comparisons can be obtained via a custom time source. Call the `JwtParserBuilder`'s `setClock`
method with an implementation of the `io.jsonwebtoken.Clock` interface. For example:
```java
Clock clock = new MyClock();
Jwts.parser().setClock(myClock) //... etc ...
Jwts.parserBuilder().setClock(myClock) //... etc ...
```
The `JwtParser`'s default `Clock` implementation simply returns `new Date()` to reflect the time when parsing occurs,
@ -1169,7 +1178,7 @@ guarantee deterministic behavior.
<a name="jws-read-decompression"></a>
#### JWS Decompression
If you used JJWT to compress a JWS and you used a custom compression algorithm, you will need to tell the `JwtParser`
If you used JJWT to compress a JWS and you used a custom compression algorithm, you will need to tell the `JwtParserBuilder`
how to resolve your `CompressionCodec` to decompress the JWT.
Please see the [Compression](#compression) section below to see how to decompress JWTs during parsing.
@ -1208,12 +1217,12 @@ parsing or configure the `JwtParser` for compression - JJWT will automatically d
### Custom Compression Codec
If however, you used your own custom compression codec when creating the JWT (via `JwtBuilder` `compressWith`), then
you need to supply the codec to the `JwtParser` using the `setCompressionCodecResolver` method. For example:
you need to supply the codec to the `JwtParserBuilder` using the `setCompressionCodecResolver` method. For example:
```java
CompressionCodecResolver ccr = new MyCompressionCodecResolver();
Jwts.parser()
Jwts.parserBuilder()
.setCompressionCodecResolver(ccr) // <----
@ -1245,30 +1254,34 @@ A `JwtBuilder` will serialize the `Header` and `Claims` maps (and potentially an
contain) to JSON with a `Serializer<Map<String, ?>>` instance. Similarly, a `JwtParser` will
deserialize JSON into the `Header` and `Claims` using a `Deserializer<Map<String, ?>>` instance.
If you don't explicitly configure a `JwtBuilder`'s `Serializer` or a `JwtParser`'s `Deserializer`, JJWT will
If you don't explicitly configure a `JwtBuilder`'s `Serializer` or a `JwtParserBuilder`'s `Deserializer`, JJWT will
automatically attempt to discover and use the following JSON implementations if found in the runtime classpath.
They are checked in order, and the first one found is used:
1. Jackson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-jackson` as a project runtime
dependency. Jackson supports POJOs as claims with full marshaling/unmarshaling as necessary.
2. JSON-Java (`org.json`): This will be used automatically if you specify `io.jsonwebtoken:jjwt-orgjson` as a
2. Gson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-gson` as a project runtime dependency.
Gson also supports POJOs as claims with full marshaling/unmarshaling as necessary.
3. JSON-Java (`org.json`): This will be used automatically if you specify `io.jsonwebtoken:jjwt-orgjson` as a
project runtime dependency.
**NOTE:** `org.json` APIs are natively enabled in Android environments so this is the recommended JSON processor for
Android applications _unless_ you want to use POJOs as claims. The `org.json` library supports simple
Object-to-JSON marshaling, but it *does not* support JSON-to-Object unmarshalling.
**If you want to use POJOs as claim values, use the `io.jsonwebtoken:jjwt-jackson` dependency** (or implement your own
Serializer and Deserializer if desired). **But beware**, Jackson will force a sizable (> 1 MB) dependency to an
Android application thus increasing the app download size for mobile users.
**If you want to use POJOs as claim values, use either the `io.jsonwebtoken:jjwt-jackson` or
`io.jsonwebtoken:jjwt-gson` dependency** (or implement your own Serializer and Deserializer if desired). **But beware**,
Jackson will force a sizable (> 1 MB) dependency to an Android application thus increasing the app download size for
mobile users.
<a name="json-custom"></a>
### Custom JSON Processor
If you don't want to use JJWT's runtime dependency approach, or just want to customize how JSON serialization and
deserialization works, you can implement the `Serializer` and `Deserializer` interfaces and specify instances of
them on the `JwtBuilder` and `JwtParser` respectively. For example:
them on the `JwtBuilder` and `JwtParserBuilder` respectively. For example:
When creating a JWT:
@ -1287,7 +1300,7 @@ When reading a JWT:
```java
Deserializer<Map<String,?>> deserializer = getMyDeserializer(); //implement me
Jwts.parser()
Jwts.parserBuilder()
.deserializeJsonWith(deserializer)
@ -1297,8 +1310,16 @@ Jwts.parser()
<a name="json-jackson"></a>
### Jackson JSON Processor
If you have an application-wide Jackson `ObjectMapper` (as is typically recommended for most applications), you can
eliminate the overhead of JJWT constructing its own `ObjectMapper` by using yours instead.
If you want to use Jackson for JSON processing, just including the `io.jsonwebtoken:jjwt-jackson` dependency as a
runtime dependency is all that is necessary in most projects, since Gradle and Maven will automatically pull in
the necessary Jackson dependencies as well.
After including this dependency, JJWT will automatically find the Jackson implementation on the runtime classpath and
use it internally for JSON parsing. There is nothing else you need to do - JJWT will automatically create a new
Jackson ObjectMapper for its needs as required.
However, if you have an application-wide Jackson `ObjectMapper` (as is typically recommended for most applications),
you can configure JJWT to use your own `ObjectMapper` instead.
You do this by declaring the `io.jsonwebtoken:jjwt-jackson` dependency with **compile** scope (not runtime
scope which is the typical JJWT default). That is:
@ -1309,7 +1330,7 @@ scope which is the typical JJWT default). That is:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.7</version>
<version>0.11.0-SNAPSHOT</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
@ -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 ...
```
<a name="json-jackson-custom-types"></a>
#### 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).
<a name="json-gson"></a>
### 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
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-gson</artifactId>
<version>0.11.0-SNAPSHOT</version>
<scope>compile</scope> <!-- Not runtime -->
</dependency>
```
**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 ...
```
<a name="base64"></a>
## 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<String, byte[]> base64UrlDecoder = getMyBase64UrlDecoder(); //implement me
Jwts.parser()
Jwts.parserBuilder()
.base64UrlDecodeWith(base64UrlEncoder)

View File

@ -298,8 +298,8 @@ public interface JwtParserBuilder {
JwtParserBuilder deserializeJsonWith(Deserializer<Map<String,?>> 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();
}

View File

@ -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");