Issue-52 Adding ability to compress/decompress. Added tests for happy path.

This commit is contained in:
josebarrueta 2015-09-23 13:21:08 -07:00
parent 56fb2c6a75
commit 19f6fcaa51
19 changed files with 581 additions and 11 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
*.class
.DS_Store
# Mobile Tools for Java (J2ME)
.mtj.tmp/

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 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;
/**
* CompressionCodec
*
* @since 0.59
*/
public interface CompressionCodec {
String getAlgorithmName();
byte[] compress(byte[] payload);
byte[] decompress(byte[] compressed);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 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;
/**
* CompressionCodecResolver
*
* @since 0.5.2
*/
public interface CompressionCodecResolver {
CompressionCodec resolveCompressionCodec(Header header);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2015 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;
/**
* Exception indicating that either compressing or decompressing an JWT body failed.
*
* @since 0.5.2
*/
public class CompressionException extends JwtException {
public CompressionException(String message) {
super(message);
}
public CompressionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -48,6 +48,9 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
/** JWT {@code Content Type} header parameter name: <code>"cty"</code> */
public static final String CONTENT_TYPE = "cty";
/** JWT {@code Compression Algorithm} header parameter name: <code>"calg"</code> */
public static final String COMPRESSION_ALGORITHM = "calg";
/**
* Returns the <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-5.1">
* <code>typ</code></a> (type) header value or {@code null} if not present.
@ -100,4 +103,8 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
*/
T setContentType(String cty);
String getCompressionAlgorithm();
T setCompressionAlgorithm(String compressionAlgorithm);
}

View File

@ -349,6 +349,15 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
*/
JwtBuilder signWith(SignatureAlgorithm alg, Key key);
/**
* Compresses the JWT body before being signed.
*
* @param codec implementation of the {@link CompressionCodec} to be used.
* @return the builder for method chaining.
* @since 0.5.2
*/
JwtBuilder compressWith(CompressionCodec codec);
/**
* Actually builds the JWT and serializes it to a compact, URL-safe string according to the
* <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-7">JWT Compact Serialization</a>

View File

@ -107,6 +107,14 @@ public interface JwtParser {
*/
JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
/**
*
* @param compressionCodecResolver
* @return the parser for method chaining.
* @since 0.5.2
*/
JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver);
/**
* Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
* otherwise.

View File

@ -51,4 +51,16 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
setValue(CONTENT_TYPE, cty);
return (T)this;
}
@Override
public String getCompressionAlgorithm() {
return getString(COMPRESSION_ALGORITHM);
}
@Override
public T setCompressionAlgorithm(String compressionAlgorithm) {
setValue(COMPRESSION_ALGORITHM, compressionAlgorithm);
return (T) this;
}
}

View File

@ -18,6 +18,7 @@ package io.jsonwebtoken.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.JwtBuilder;
@ -48,6 +49,8 @@ public class DefaultJwtBuilder implements JwtBuilder {
private Key key;
private byte[] keyBytes;
private CompressionCodec compressionCodec;
@Override
public JwtBuilder setHeader(Header header) {
this.header = header;
@ -113,6 +116,13 @@ public class DefaultJwtBuilder implements JwtBuilder {
return this;
}
@Override
public JwtBuilder compressWith(CompressionCodec compressionCodec) {
Assert.notNull(compressionCodec, "compressionCodec cannot be null");
this.compressionCodec = compressionCodec;
return this;
}
@Override
public JwtBuilder setPayload(String payload) {
this.payload = payload;
@ -279,11 +289,30 @@ public class DefaultJwtBuilder implements JwtBuilder {
jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
}
if (compressionCodec != null) {
jwsHeader.setCompressionAlgorithm(compressionCodec.getAlgorithmName());
}
String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");
String base64UrlEncodedBody = this.payload != null ?
TextCodec.BASE64URL.encode(this.payload) :
base64UrlEncode(claims, "Unable to serialize claims object to json.");
String base64UrlEncodedBody;
if (compressionCodec != null) {
byte[] bytes;
try {
bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Unable to serialize claims object to json.");
}
base64UrlEncodedBody = TextCodec.BASE64URL.encode(compressionCodec.compress(bytes));
} else {
base64UrlEncodedBody = this.payload != null ?
TextCodec.BASE64URL.encode(this.payload) :
base64UrlEncode(claims, "Unable to serialize claims object to json.");
}
String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;
@ -311,17 +340,18 @@ public class DefaultJwtBuilder implements JwtBuilder {
}
protected String base64UrlEncode(Object o, String errMsg) {
String s;
byte[] bytes;
try {
s = toJson(o);
bytes = toJson(o);
} catch (JsonProcessingException e) {
throw new IllegalStateException(errMsg, e);
}
return TextCodec.BASE64URL.encode(s);
return TextCodec.BASE64URL.encode(bytes);
}
protected String toJson(Object o) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsString(o);
protected byte[] toJson(Object object) throws JsonProcessingException {
return OBJECT_MAPPER.writeValueAsBytes(object);
}
}

View File

@ -17,11 +17,12 @@ package io.jsonwebtoken.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtHandler;
import io.jsonwebtoken.JwtHandlerAdapter;
@ -30,7 +31,9 @@ import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.PrematureJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
import io.jsonwebtoken.lang.Assert;
@ -58,6 +61,8 @@ public class DefaultJwtParser implements JwtParser {
private SigningKeyResolver signingKeyResolver;
private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver();
@Override
public JwtParser setSigningKey(byte[] key) {
Assert.notEmpty(key, "signing key cannot be null or empty.");
@ -86,6 +91,13 @@ public class DefaultJwtParser implements JwtParser {
return this;
}
@Override
public JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) {
Assert.notNull(compressionCodecResolver, "compressionCodecResolver cannot be null.");
this.compressionCodecResolver = compressionCodecResolver;
return this;
}
@Override
public boolean isSigned(String jwt) {
@ -157,6 +169,8 @@ public class DefaultJwtParser implements JwtParser {
// =============== Header =================
Header header = null;
CompressionCodec compressionCodec = null;
if (base64UrlEncodedHeader != null) {
String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
Map<String, Object> m = readValue(origValue);
@ -166,10 +180,18 @@ public class DefaultJwtParser implements JwtParser {
} else {
header = new DefaultHeader(m);
}
compressionCodec = compressionCodecResolver.resolveCompressionCodec(header);
}
// =============== Body =================
String payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload);
String payload;
if (compressionCodec != null) {
byte[] decompressed = compressionCodec.decompress(TextCodec.BASE64URL.decode(base64UrlEncodedPayload));
payload = new String(decompressed, Strings.UTF_8);
} else {
payload = TextCodec.BASE64URL.decodeToString(base64UrlEncodedPayload);
}
Claims claims = null;

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2015 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.impl.compression;
import io.jsonwebtoken.CompressionCodec;
/**
* CompressionCodecs exposes default implementation of the {@link CompressionCodec} interface.
*
* @since 0.5.2
*/
public abstract class CompressionCodecs {
private CompressionCodecs(){}
public static final CompressionCodec DEFLATE = new DeflateCompressionCodec();
public static final CompressionCodec GZIP = new GzipCompressionCodec();
}

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 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.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Strings;
/**
* DefaultCompressionCodecResolver
*
* @since 0.5.2
*/
public class DefaultCompressionCodecResolver implements CompressionCodecResolver {
@Override
public CompressionCodec resolveCompressionCodec(Header header) {
Assert.notNull(header, "header cannot be null.");
String cmpAlg = header.getCompressionAlgorithm();
if (!Strings.hasText(cmpAlg)) {
return null;
}
if (CompressionCodecs.DEFLATE.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
return CompressionCodecs.DEFLATE;
}
if (CompressionCodecs.GZIP.getAlgorithmName().equalsIgnoreCase(cmpAlg)) {
return CompressionCodecs.GZIP;
}
throw new CompressionException("Unsupported compression algorithm '" + cmpAlg + "'");
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 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.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream;
/**
* DeflateCompressionCodec
*
* @since 0.5.2
*/
public class DeflateCompressionCodec implements CompressionCodec {
private static final String DEFLATE = "DEF";
@Override
public String getAlgorithmName() {
return DEFLATE;
}
@Override
public byte[] compress(byte[] payload) {
Assert.notNull(payload, "payload cannot be null.");
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
ByteArrayOutputStream outputStream = null;
DeflaterOutputStream deflaterOutputStream = null;
try {
outputStream = new ByteArrayOutputStream();
deflaterOutputStream = new DeflaterOutputStream(outputStream, deflater, true);
deflaterOutputStream.write(payload, 0, payload.length);
deflaterOutputStream.flush();
return outputStream.toByteArray();
} catch (IOException e) {
throw new CompressionException("Unable to compress payload.", e);
} finally {
Objects.nullSafeClose(outputStream, deflaterOutputStream);
}
}
@Override
public byte[] decompress(byte[] compressed) {
Assert.notNull(compressed, "compressed cannot be null.");
InflaterOutputStream inflaterOutputStream = null;
ByteArrayOutputStream decompressedOutputStream = null;
try {
decompressedOutputStream = new ByteArrayOutputStream();
inflaterOutputStream = new InflaterOutputStream(decompressedOutputStream);
inflaterOutputStream.write(compressed);
inflaterOutputStream.flush();
return decompressedOutputStream.toByteArray();
} catch (IOException e) {
throw new CompressionException("Unable to decompress compressed payload.", e);
} finally {
Objects.nullSafeClose(decompressedOutputStream, inflaterOutputStream);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2015 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.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Objects;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* GzipCompressionCodec
*
* @since 0.5.2
*/
public class GzipCompressionCodec implements CompressionCodec {
private static final String GZIP = "GZIP";
@Override
public String getAlgorithmName() {
return GZIP;
}
@Override
public byte[] compress(byte[] payload) {
Assert.notNull(payload, "payload cannot be null.");
ByteArrayOutputStream outputStream = null;
GZIPOutputStream gzipOutputStream = null;
try {
outputStream = new ByteArrayOutputStream();
gzipOutputStream = new GZIPOutputStream(outputStream, true);
gzipOutputStream.write(payload, 0, payload.length);
gzipOutputStream.finish();
return outputStream.toByteArray();
} catch (IOException e) {
throw new CompressionException("Unable to compress payload.", e);
} finally {
Objects.nullSafeClose(outputStream, gzipOutputStream);
}
}
@Override
public byte[] decompress(byte[] compressed) {
Assert.notNull(compressed, "compressed cannot be null.");
byte[] buffer = new byte[512];
ByteArrayOutputStream outputStream = null;
GZIPInputStream gzipInputStream = null;
ByteArrayInputStream inputStream = null;
try {
inputStream = new ByteArrayInputStream(compressed);
gzipInputStream = new GZIPInputStream(inputStream);
outputStream = new ByteArrayOutputStream();
int read;
while ((read = gzipInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
return outputStream.toByteArray();
} catch (IOException e) {
throw new CompressionException("Unable to decompress compressed payload.", e);
} finally {
Objects.nullSafeClose(inputStream, gzipInputStream, outputStream);
}
}
}

View File

@ -15,6 +15,8 @@
*/
package io.jsonwebtoken.lang;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;
@ -905,4 +907,19 @@ public abstract class Objects {
return sb.toString();
}
public static void nullSafeClose(Closeable... closeables) {
if (closeables == null) {
return;
}
for (Closeable closeable : closeables) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
//Ignore the exception during close.
}
}
}
}
}

View File

@ -15,6 +15,7 @@
*/
package io.jsonwebtoken.lang;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -40,6 +41,8 @@ public abstract class Strings {
private static final char EXTENSION_SEPARATOR = '.';
public static final Charset UTF_8 = Charset.forName("UTF-8");
//---------------------------------------------------------------------
// General convenience methods for working with Strings
//---------------------------------------------------------------------

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2015 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
import org.junit.Test
import static org.junit.Assert.assertEquals
class CompressionExceptionTest {
@Test
void testDefaultConstructor() {
def exception = new CompressionException("my message")
assertEquals "my message", exception.getMessage()
}
@Test
void testConstructorWithCause() {
def ioException = new IOException("root error")
def exception = new CompressionException("wrapping", ioException)
assertEquals "wrapping", exception.getMessage()
assertEquals ioException, exception.getCause()
}
}

View File

@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import io.jsonwebtoken.impl.DefaultHeader
import io.jsonwebtoken.impl.DefaultJwsHeader
import io.jsonwebtoken.impl.TextCodec
import io.jsonwebtoken.impl.compression.CompressionCodecs
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
import io.jsonwebtoken.impl.crypto.MacProvider
import io.jsonwebtoken.impl.crypto.RsaProvider
@ -186,6 +187,16 @@ class JwtsTest {
}
}
@Test
void testWithInvalidCompressionAlgorithm() {
try {
Jwts.builder().setHeaderParam(Header.COMPRESSION_ALGORITHM, "CUSTOM").setId("andId").compact()
} catch (CompressionException e) {
assertEquals "Unsupported compression algorithm 'CUSTOM'", e.getMessage()
}
}
@Test
void testConvenienceIssuer() {
String compact = Jwts.builder().setIssuer("Me").compact();
@ -320,6 +331,67 @@ class JwtsTest {
assertNull claims.getId()
}
@Test
void testCompressedJwtWithDeflate() {
byte[] key = MacProvider.generateKey().getEncoded()
String id = UUID.randomUUID().toString()
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
.claim("state", "hello this is an amazing jwt").compressWith(CompressionCodecs.DEFLATE).compact()
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
Claims claims = jws.body
assertEquals "DEF", jws.header.getCompressionAlgorithm()
assertEquals id, claims.getId()
assertEquals "an audience", claims.getAudience()
assertEquals "hello this is an amazing jwt", claims.state
}
@Test
void testCompressedJwtWithGZIP() {
byte[] key = MacProvider.generateKey().getEncoded()
String id = UUID.randomUUID().toString()
String compact = Jwts.builder().setId(id).setAudience("an audience").signWith(SignatureAlgorithm.HS256, key)
.claim("state", "hello this is an amazing jwt").compressWith(CompressionCodecs.GZIP).compact()
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
Claims claims = jws.body
assertEquals "GZIP", jws.header.getCompressionAlgorithm()
assertEquals id, claims.getId()
assertEquals "an audience", claims.getAudience()
assertEquals "hello this is an amazing jwt", claims.state
}
@Test
void testCompressStringPayloadWithDeflate() {
byte[] key = MacProvider.generateKey().getEncoded()
String payload = "this is my test for a payload"
String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, key)
.compressWith(CompressionCodecs.DEFLATE).compact()
def jws = Jwts.parser().setSigningKey(key).parsePlaintextJws(compact)
String parsed = jws.body
assertEquals "DEF", jws.header.getCompressionAlgorithm()
assertEquals "this is my test for a payload", parsed
}
@Test
void testHS256() {
testHmac(SignatureAlgorithm.HS256);

View File

@ -173,7 +173,7 @@ class DefaultJwtBuilderTest {
def b = new DefaultJwtBuilder() {
@Override
protected String toJson(Object o) throws JsonProcessingException {
protected byte[] toJson(Object o) throws JsonProcessingException {
throw new JsonMappingException('foo')
}
}