Skip to content

Add JwtHandler for JWT type-specific handling #5

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 10 commits into from
Sep 27, 2014
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Note: JJWT depends on Jackson 2.x. If you're already using an older version of

## Usage

Most complexity is hidden behind convenient and readable Builder chaining calls. Here's an example:
Most complexity is hidden behind a convenient and readable builder-based [fluent interface](http://en.wikipedia.org/wiki/Fluent_interface), great for relying on IDE auto-completion to write code quickly. Here's an example:

```java
import io.jsonwebtoken.Jwts;
Expand Down Expand Up @@ -108,6 +108,86 @@ String compactJwt = Jwts.builder().setSubject("Joe").signWith(HS256, key).compac

A Claims instance based on the specified claims will be created and set as the JWT's payload automatically.

#### Type-safe handling for JWT and JWS with generics

The following < 0.2 code produced a JWT as expected:

```java
Jwt jwt = Jwts.parser().setSigningKey(key).parse(compact);
```

But you couldn't easily determine if the `jwt` was a `JWT` or `JWS` instance or if the body was a `Claims` instance or a plaintext `String` without resorting to a bunch of yucky `instanceof` checks. In 0.2, we introduce the `JwtHandler` when you don't know the exact format of the compact JWT string ahead of time, and parsing convenience methods when you do.

##### JwtHandler

If you do not know the format of the compact JWT string at the time you try to parse it, you can determine what type it is after parsing by providing a `JwtHandler` instance to the `JwtParser` with the new `parse(String compactJwt, JwtHandler handler)` method. For example:

```java
T returnVal = Jwts.parser().setSigningKey(key).parse(compact, new JwtHandler<T>() {
@Override
public Object onPlaintextJwt(Jwt<Header, String> jwt) {
//the JWT parsed was an unsigned plaintext JWT
//inspect it, then return an instance of T (see returnVal above)
}

@Override
public Object onClaimsJwt(Jwt<Header, Claims> jwt) {
//the JWT parsed was an unsigned Claims JWT
//inspect it, then return an instance of T (see returnVal above)
}

@Override
public Object onPlaintextJws(Jws<String> jws) {
//the JWT parsed was a signed plaintext JWS
//inspect it, then return an instance of T (see returnVal above)
}

@Override
public Object onClaimsJws(Jws<Claims> jws) {
//the JWT parsed was a signed Claims JWS
//inspect it, then return an instance of T (see returnVal above)
}
});
```

Of course, if you know you'll only have to parse a subset of the above, you can use the `JwtHandlerAdapter` and implement only the methods you need. For example:

```java
T returnVal = Jwts.parser().setSigningKey(key).parse(plaintextJwt, new JwtHandlerAdapter<Jwt<Header, T>>() {
@Override
public Object onPlaintextJws(Jws<String> jws) {
//the JWT parsed was a signed plaintext JWS
//inspect it, then return an instance of T (see returnVal above)
}

@Override
public Object onClaimsJws(Jws<Claims> jws) {
//the JWT parsed was a signed Claims JWS
//inspect it, then return an instance of T (see returnVal above)
}
});
```

##### Known Type convenience parse methods

If, unlike above, you are confident of the compact string format and know which type of JWT or JWS it will produce, you can just use one of the 4 new convenience parsing methods to get exactly the type of JWT or JWS you know exists. For example:

```java

//for a known plaintext jwt string:
Jwt<Header,String> jwt = Jwts.parser().parsePlaintextJwt(compact);

//for a known Claims JWT string:
Jwt<Header,Claims> jwt = Jwts.parser().parseClaimsJwt(compact);

//for a known signed plaintext JWT (aka a plaintext JWS):
Jws<String> jws = Jwts.parser().setSigningKey(key).parsePlaintextJws(compact);

//for a known signed Claims JWT (aka a Claims JWS):
Jws<Claims> jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact);

```

<a name="olderJackson"></a>
#### Already using an older Jackson dependency?

Expand Down
68 changes: 68 additions & 0 deletions src/main/java/io/jsonwebtoken/JwtHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2014 jsonwebtoken.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jsonwebtoken;

/**
* A JwtHandler is invoked by a {@link io.jsonwebtoken.JwtParser JwtParser} after parsing a JWT to indicate the exact
* type of JWT or JWS parsed.
*
* @param <T> the type of object to return to the parser caller after handling the parsed JWT.
* @since 0.2
*/
public interface JwtHandler<T> {

/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
* a plaintext JWT. A plaintext JWT has a String (non-JSON) body payload and it is not cryptographically signed.
*
* @param jwt the parsed plaintext JWT
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
*/
T onPlaintextJwt(Jwt<Header, String> jwt);

/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
* a Claims JWT. A Claims JWT has a {@link Claims} body and it is not cryptographically signed.
*
* @param jwt the parsed claims JWT
* @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.
*/
T onClaimsJwt(Jwt<Header, Claims> jwt);

/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
* a plaintext JWS. A plaintext JWS is a JWT with a String (non-JSON) body (payload) that has been
* cryptographically signed.
*
* <p>This method will only be invoked if the cryptographic signature can be successfully verified.</p>
*
* @param jws the parsed plaintext JWS
* @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.
*/
T onPlaintextJws(Jws<String> jws);

/**
* This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is
* a valid Claims JWS. A Claims JWS is a JWT with a {@link Claims} body that has been cryptographically signed.
*
* <p>This method will only be invoked if the cryptographic signature can be successfully verified.</p>
*
* @param jws the parsed claims JWS
* @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.
*/
T onClaimsJws(Jws<Claims> jws);

}
52 changes: 52 additions & 0 deletions src/main/java/io/jsonwebtoken/JwtHandlerAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 jsonwebtoken.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jsonwebtoken;

/**
* An <a href="http://en.wikipedia.org/wiki/Adapter_pattern">Adapter</a> implementation of the
* {@link JwtHandler} interface that allows for anonymous subclasses to process only the JWT results that are
* known/expected for a particular use case.
*
* <p>All of the methods in this implementation throw exceptions: overridden methods represent
* scenarios expected by calling code in known situations. It would be unexpected to receive a JWS or JWT that did
* not match parsing expectations, so all non-overridden methods throw exceptions to indicate that the JWT
* input was unexpected.</p>
*
* @param <T> the type of object to return to the parser caller after handling the parsed JWT.
* @since 0.2
*/
public class JwtHandlerAdapter<T> implements JwtHandler<T> {

@Override
public T onPlaintextJwt(Jwt<Header, String> jwt) {
throw new UnsupportedJwtException("Unsigned plaintext JWTs are not supported.");
}

@Override
public T onClaimsJwt(Jwt<Header, Claims> jwt) {
throw new UnsupportedJwtException("Unsigned Claims JWTs are not supported.");
}

@Override
public T onPlaintextJws(Jws<String> jws) {
throw new UnsupportedJwtException("Signed plaintext JWSs are not supported.");
}

@Override
public T onClaimsJws(Jws<Claims> jws) {
throw new UnsupportedJwtException("Signed Claims JWSs are not supported.");
}
}
Loading