Skip to content

Using ObjectReader to cache Jackson bindings when parsing JWT #194

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion lib/src/main/java/com/auth0/jwt/JWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.impl.JWTParser;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;

@SuppressWarnings("WeakerAccess")
public abstract class JWT {
public class JWT {

private final JWTParser parser;

/**
* Constructs a new instance of the JWT library. Use this if you need to decode many JWT
* tokens on the fly and do not wish to instantiate a new parser for each invocation.
*/
public JWT() {
parser = new JWTParser();
}

/**
* Decode a given Json Web Token.
* <p>
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
*
* @param token with jwt format as string.
* @return a decoded JWT.
* @throws JWTDecodeException if any part of the token contained an invalid jwt or JSON format of each of the jwt parts.
*/
public DecodedJWT decodeJwt(String token) throws JWTDecodeException {
return new JWTDecoder(parser, token);
}

/**
* Decode a given Json Web Token.
Expand Down
5 changes: 4 additions & 1 deletion lib/src/main/java/com/auth0/jwt/JWTDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ final class JWTDecoder implements DecodedJWT {
private final Payload payload;

JWTDecoder(String jwt) throws JWTDecodeException {
this(new JWTParser(), jwt);
}

JWTDecoder(JWTParser converter, String jwt) throws JWTDecodeException {
parts = TokenUtils.splitToken(jwt);
final JWTParser converter = new JWTParser();
String headerJson;
String payloadJson;
try {
Expand Down
44 changes: 29 additions & 15 deletions lib/src/main/java/com/auth0/jwt/impl/JWTParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,50 @@
import com.auth0.jwt.interfaces.Payload;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.IOException;

public class JWTParser implements JWTPartsParser {
private ObjectMapper mapper;
private final ObjectReader payloadReader;
private final ObjectReader headerReader;

public JWTParser() {
this(getDefaultObjectMapper());
}

JWTParser(ObjectMapper mapper) {
addDeserializers(mapper);
this.mapper = mapper;
this.payloadReader = mapper.readerFor(Payload.class);
this.headerReader = mapper.readerFor(Header.class);
}

@Override
public Payload parsePayload(String json) throws JWTDecodeException {
return convertFromJSON(json, Payload.class);
if (json == null) {
throw decodeException();
}

try {
return payloadReader.readValue(json);
} catch (IOException e) {
throw decodeException(json);
}
}

@Override
public Header parseHeader(String json) throws JWTDecodeException {
return convertFromJSON(json, Header.class);
if (json == null) {
throw decodeException();
}

try {
return headerReader.readValue(json);
} catch (IOException e) {
throw decodeException(json);
}
}

private void addDeserializers(ObjectMapper mapper) {
Expand All @@ -47,16 +66,11 @@ static ObjectMapper getDefaultObjectMapper() {
return mapper;
}

@SuppressWarnings("WeakerAccess")
<T> T convertFromJSON(String json, Class<T> tClazz) throws JWTDecodeException {
JWTDecodeException exception = new JWTDecodeException(String.format("The string '%s' doesn't have a valid JSON format.", json));
if (json == null) {
throw exception;
}
try {
return mapper.readValue(json, tClazz);
} catch (IOException e) {
throw exception;
}
private static JWTDecodeException decodeException() {
return decodeException(null);
}

private static JWTDecodeException decodeException(String json) {
return new JWTDecodeException(String.format("The string '%s' doesn't have a valid JSON format.", json));
}
}
18 changes: 18 additions & 0 deletions lib/src/test/java/com/auth0/jwt/JWTTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ public void shouldDecodeAStringToken() throws Exception {
assertThat(jwt, is(notNullValue()));
}

@Test
public void shouldDecodeAStringTokenUsingInstance() throws Exception {
String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M";
JWT jwt = new JWT();
DecodedJWT decodedJWT = jwt.decodeJwt(token);

assertThat(decodedJWT, is(notNullValue()));
}

// getToken
@Test
public void shouldGetStringToken() throws Exception {
Expand All @@ -55,6 +64,15 @@ public void shouldGetStringToken() throws Exception {
assertThat(jwt.getToken(), is("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"));
}

// getToken
@Test
public void shouldGetStringTokenUsingInstance() throws Exception {
JWT jwt = new JWT();
DecodedJWT decodedJWT = jwt.decodeJwt("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ");
assertThat(decodedJWT, is(notNullValue()));
assertThat(decodedJWT.getToken(), is(notNullValue()));
assertThat(decodedJWT.getToken(), is("eyJhbGciOiJIUzI1NiJ9.e30.XmNK3GpH3Ys_7wsYBfq4C3M6goz71I7dTgUkuIa5lyQ"));
}

// Verify

Expand Down
39 changes: 24 additions & 15 deletions lib/src/test/java/com/auth0/jwt/impl/JWTParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.auth0.jwt.interfaces.Payload;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.junit.Before;
import org.junit.Rule;
Expand All @@ -17,6 +18,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class JWTParserTest {

Expand Down Expand Up @@ -47,10 +49,12 @@ public void shouldAddDeserializers() throws Exception {
@Test
public void shouldParsePayload() throws Exception {
ObjectMapper mapper = mock(ObjectMapper.class);
ObjectReader reader = mock(ObjectReader.class);
when(mapper.readerFor(Payload.class)).thenReturn(reader);
JWTParser parser = new JWTParser(mapper);
parser.parsePayload("{}");

verify(mapper).readValue("{}", Payload.class);
verify(reader).readValue("{}");
}

@Test
Expand All @@ -65,10 +69,12 @@ public void shouldThrowOnInvalidPayload() throws Exception {
@Test
public void shouldParseHeader() throws Exception {
ObjectMapper mapper = mock(ObjectMapper.class);
ObjectReader reader = mock(ObjectReader.class);
when(mapper.readerFor(Header.class)).thenReturn(reader);
JWTParser parser = new JWTParser(mapper);
parser.parseHeader("{}");

verify(mapper).readValue("{}", Header.class);
verify(reader).readValue("{}");
}

@Test
Expand All @@ -81,27 +87,30 @@ public void shouldThrowOnInvalidHeader() throws Exception {
}

@Test
public void shouldConvertFromValidJSON() throws Exception {
String json = "\r\n { \r\n } \r\n";
Object object = parser.convertFromJSON(json, Object.class);
assertThat(object, is(notNullValue()));
public void shouldThrowWhenConvertingHeaderIfNullJson() throws Exception {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The string 'null' doesn't have a valid JSON format.");
parser.parseHeader(null);
}

@Test
public void shouldThrowWhenConvertingHeaderFromInvalidJson() throws Exception {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The string '}{' doesn't have a valid JSON format.");
parser.parseHeader("}{");
}

@Test
public void shouldThrowWhenConvertingIfNullJson() throws Exception {
public void shouldThrowWhenConvertingPayloadIfNullJson() throws Exception {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The string 'null' doesn't have a valid JSON format.");
String json = null;
Object object = parser.convertFromJSON(json, Object.class);
assertThat(object, is(nullValue()));
parser.parsePayload(null);
}

@Test
public void shouldThrowWhenConvertingFromInvalidJson() throws Exception {
public void shouldThrowWhenConvertingPayloadFromInvalidJson() throws Exception {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The string '}{' doesn't have a valid JSON format.");
String json = "}{";
Object object = parser.convertFromJSON(json, Object.class);
assertThat(object, is(nullValue()));
parser.parsePayload("}{");
}
}
}