mirror of https://github.com/jwtk/jjwt.git
commit
bb471be0e9
|
@ -1,4 +1,5 @@
|
||||||
*.class
|
*.class
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Mobile Tools for Java (J2ME)
|
# Mobile Tools for Java (J2ME)
|
||||||
.mtj.tmp/
|
.mtj.tmp/
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines how to compress and decompress byte arrays.
|
||||||
|
*
|
||||||
|
* @since 0.5.2
|
||||||
|
* @see io.jsonwebtoken.impl.compression.DeflateCompressionCodec
|
||||||
|
* @see io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||||
|
*/
|
||||||
|
public interface CompressionCodec {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The algorithm name that would appear in the JWT header.
|
||||||
|
* @return the algorithm name that would appear in the JWT header
|
||||||
|
*/
|
||||||
|
String getAlgorithmName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a byte array and returns a compressed version.
|
||||||
|
* @param payload bytes to compress
|
||||||
|
* @return compressed bytes
|
||||||
|
*/
|
||||||
|
byte[] compress(byte[] payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a compressed byte array and returns a decompressed version.
|
||||||
|
* @param compressed compressed bytes
|
||||||
|
* @return decompressed bytes
|
||||||
|
*/
|
||||||
|
byte[] decompress(byte[] compressed);
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves "calg" header to an implementation of CompressionCodec.
|
||||||
|
*
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
public interface CompressionCodecResolver {
|
||||||
|
/**
|
||||||
|
* Examines the header and returns a CompressionCodec if it finds one that it recognizes.
|
||||||
|
* @param header of the JWT
|
||||||
|
* @return CompressionCodec matching the "calg" header, or null if there is no "calg" header.
|
||||||
|
*/
|
||||||
|
CompressionCodec resolveCompressionCodec(Header header);
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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> */
|
/** JWT {@code Content Type} header parameter name: <code>"cty"</code> */
|
||||||
public static final String CONTENT_TYPE = "cty";
|
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">
|
* 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.
|
* <code>typ</code></a> (type) header value or {@code null} if not present.
|
||||||
|
@ -100,4 +103,25 @@ public interface Header<T extends Header<T>> extends Map<String,Object> {
|
||||||
*/
|
*/
|
||||||
T setContentType(String cty);
|
T setContentType(String cty);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JWT <code>calg</code> (Compression Algorithm) header value or {@code null} if not present.
|
||||||
|
*
|
||||||
|
* @return the {@code calg} header parameter value or {@code null} if not present.
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
String getCompressionAlgorithm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the JWT <code>calg</code> (Compression Algorithm) header parameter value. A {@code null} value will remove
|
||||||
|
* the property from the JSON map.
|
||||||
|
* <p>
|
||||||
|
* <p>The compression algorithm is NOT part of the <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25>JWT specification</a>
|
||||||
|
* and must be used carefully since, is not expected that other libraries (including previous versions of this one)
|
||||||
|
* be able to deserialize a compressed JTW body correctly. </p>
|
||||||
|
*
|
||||||
|
* @param calg the JWT compression algorithm {@code calg} value or {@code null} to remove the property from the JSON map.
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
T setCompressionAlgorithm(String calg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,6 +349,18 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
|
||||||
*/
|
*/
|
||||||
JwtBuilder signWith(SignatureAlgorithm alg, Key key);
|
JwtBuilder signWith(SignatureAlgorithm alg, Key key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses the JWT body using the {@link CompressionCodec} passed as argument.
|
||||||
|
*
|
||||||
|
* Note: Compression is not part of the Json Web Token specification and is not expected that other libraries (including
|
||||||
|
* older versions of this one) are able to consume a compressed JWT body correctly.
|
||||||
|
*
|
||||||
|
* @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
|
* 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>
|
* <a href="https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-25#section-7">JWT Compact Serialization</a>
|
||||||
|
|
|
@ -205,6 +205,16 @@ public interface JwtParser {
|
||||||
*/
|
*/
|
||||||
JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
|
JwtParser setSigningKeyResolver(SigningKeyResolver signingKeyResolver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to verify
|
||||||
|
* a decompress the JWT body. If the parsed JWT is not compressed, this resolver si not used.
|
||||||
|
*
|
||||||
|
* @param compressionCodecResolver the compression codec resolver used to decompress the JWT body.
|
||||||
|
* @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}
|
* Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
|
||||||
* otherwise.
|
* otherwise.
|
||||||
|
|
|
@ -51,4 +51,16 @@ public class DefaultHeader<T extends Header<T>> extends JwtMap implements Header
|
||||||
setValue(CONTENT_TYPE, cty);
|
setValue(CONTENT_TYPE, cty);
|
||||||
return (T)this;
|
return (T)this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCompressionAlgorithm() {
|
||||||
|
return getString(COMPRESSION_ALGORITHM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T setCompressionAlgorithm(String compressionAlgorithm) {
|
||||||
|
setValue(COMPRESSION_ALGORITHM, compressionAlgorithm);
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package io.jsonwebtoken.impl;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.CompressionCodec;
|
||||||
import io.jsonwebtoken.Header;
|
import io.jsonwebtoken.Header;
|
||||||
import io.jsonwebtoken.JwsHeader;
|
import io.jsonwebtoken.JwsHeader;
|
||||||
import io.jsonwebtoken.JwtBuilder;
|
import io.jsonwebtoken.JwtBuilder;
|
||||||
|
@ -48,6 +49,8 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
private Key key;
|
private Key key;
|
||||||
private byte[] keyBytes;
|
private byte[] keyBytes;
|
||||||
|
|
||||||
|
private CompressionCodec compressionCodec;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JwtBuilder setHeader(Header header) {
|
public JwtBuilder setHeader(Header header) {
|
||||||
this.header = header;
|
this.header = header;
|
||||||
|
@ -113,6 +116,13 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtBuilder compressWith(CompressionCodec compressionCodec) {
|
||||||
|
Assert.notNull(compressionCodec, "compressionCodec cannot be null");
|
||||||
|
this.compressionCodec = compressionCodec;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JwtBuilder setPayload(String payload) {
|
public JwtBuilder setPayload(String payload) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
|
@ -279,11 +289,30 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
|
jwsHeader.setAlgorithm(SignatureAlgorithm.NONE.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compressionCodec != null) {
|
||||||
|
jwsHeader.setCompressionAlgorithm(compressionCodec.getAlgorithmName());
|
||||||
|
}
|
||||||
|
|
||||||
String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");
|
String base64UrlEncodedHeader = base64UrlEncode(jwsHeader, "Unable to serialize header to json.");
|
||||||
|
|
||||||
String base64UrlEncodedBody = this.payload != null ?
|
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) :
|
TextCodec.BASE64URL.encode(this.payload) :
|
||||||
base64UrlEncode(claims, "Unable to serialize claims object to json.");
|
base64UrlEncode(claims, "Unable to serialize claims object to json.");
|
||||||
|
}
|
||||||
|
|
||||||
String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;
|
String jwt = base64UrlEncodedHeader + JwtParser.SEPARATOR_CHAR + base64UrlEncodedBody;
|
||||||
|
|
||||||
|
@ -311,17 +340,18 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String base64UrlEncode(Object o, String errMsg) {
|
protected String base64UrlEncode(Object o, String errMsg) {
|
||||||
String s;
|
byte[] bytes;
|
||||||
try {
|
try {
|
||||||
s = toJson(o);
|
bytes = toJson(o);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new IllegalStateException(errMsg, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ package io.jsonwebtoken.impl;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.jsonwebtoken.ClaimJwtException;
|
import io.jsonwebtoken.ClaimJwtException;
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.CompressionCodec;
|
||||||
|
import io.jsonwebtoken.CompressionCodecResolver;
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
import io.jsonwebtoken.Header;
|
import io.jsonwebtoken.Header;
|
||||||
import io.jsonwebtoken.IncorrectClaimException;
|
import io.jsonwebtoken.IncorrectClaimException;
|
||||||
|
@ -34,7 +36,9 @@ import io.jsonwebtoken.MalformedJwtException;
|
||||||
import io.jsonwebtoken.PrematureJwtException;
|
import io.jsonwebtoken.PrematureJwtException;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
import io.jsonwebtoken.SignatureException;
|
import io.jsonwebtoken.SignatureException;
|
||||||
|
import io.jsonwebtoken.SigningKeyResolver;
|
||||||
import io.jsonwebtoken.UnsupportedJwtException;
|
import io.jsonwebtoken.UnsupportedJwtException;
|
||||||
|
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver;
|
||||||
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
|
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;
|
||||||
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
import io.jsonwebtoken.impl.crypto.JwtSignatureValidator;
|
||||||
import io.jsonwebtoken.lang.Assert;
|
import io.jsonwebtoken.lang.Assert;
|
||||||
|
@ -62,6 +66,8 @@ public class DefaultJwtParser implements JwtParser {
|
||||||
|
|
||||||
private SigningKeyResolver signingKeyResolver;
|
private SigningKeyResolver signingKeyResolver;
|
||||||
|
|
||||||
|
private CompressionCodecResolver compressionCodecResolver = new DefaultCompressionCodecResolver();
|
||||||
|
|
||||||
Claims expectedClaims = new DefaultClaims();
|
Claims expectedClaims = new DefaultClaims();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -150,6 +156,13 @@ public class DefaultJwtParser implements JwtParser {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JwtParser setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver) {
|
||||||
|
Assert.notNull(compressionCodecResolver, "compressionCodecResolver cannot be null.");
|
||||||
|
this.compressionCodecResolver = compressionCodecResolver;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSigned(String jwt) {
|
public boolean isSigned(String jwt) {
|
||||||
|
|
||||||
|
@ -221,6 +234,8 @@ public class DefaultJwtParser implements JwtParser {
|
||||||
// =============== Header =================
|
// =============== Header =================
|
||||||
Header header = null;
|
Header header = null;
|
||||||
|
|
||||||
|
CompressionCodec compressionCodec = null;
|
||||||
|
|
||||||
if (base64UrlEncodedHeader != null) {
|
if (base64UrlEncodedHeader != null) {
|
||||||
String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
|
String origValue = TextCodec.BASE64URL.decodeToString(base64UrlEncodedHeader);
|
||||||
Map<String, Object> m = readValue(origValue);
|
Map<String, Object> m = readValue(origValue);
|
||||||
|
@ -230,10 +245,18 @@ public class DefaultJwtParser implements JwtParser {
|
||||||
} else {
|
} else {
|
||||||
header = new DefaultHeader(m);
|
header = new DefaultHeader(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compressionCodec = compressionCodecResolver.resolveCompressionCodec(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============== Body =================
|
// =============== 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;
|
Claims claims = null;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* 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 java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class that asserts arguments and wraps IOException with CompressionException.
|
||||||
|
*
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
public abstract class BaseCompressionCodec implements CompressionCodec {
|
||||||
|
/**
|
||||||
|
* Implement this method to do the actual work of compressing the payload
|
||||||
|
* @param payload the bytes to compress
|
||||||
|
* @return the compressed bytes
|
||||||
|
* @throws IOException if the compression causes an IOException
|
||||||
|
*/
|
||||||
|
protected abstract byte[] doCompress(byte[] payload) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that payload is not null and calls doCompress
|
||||||
|
* @param payload bytes to compress
|
||||||
|
* @return compressed bytes
|
||||||
|
* @throws CompressionException if doCompress throws an IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final byte[] compress(byte[] payload) {
|
||||||
|
Assert.notNull(payload, "payload cannot be null.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return doCompress(payload);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompressionException("Unable to compress payload.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the compressed bytes is not null and calls doDecompress
|
||||||
|
* @param compressed compressed bytes
|
||||||
|
* @return decompressed bytes
|
||||||
|
* @throws CompressionException if doCompress throws an IOException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final byte[] decompress(byte[] compressed) {
|
||||||
|
Assert.notNull(compressed, "compressed bytes cannot be null.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return doDecompress(compressed);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompressionException("Unable to decompress bytes.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement this method to do the actual work of decompressing the compressed bytes.
|
||||||
|
* @param compressed compressed bytes
|
||||||
|
* @return decompressed bytes
|
||||||
|
* @throws IOException if the decompression runs into an IO problem
|
||||||
|
*/
|
||||||
|
protected abstract byte[] doDecompress(byte[] compressed) throws IOException;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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 interface CompressionCodecs {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Codec implementing the <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
|
||||||
|
*/
|
||||||
|
CompressionCodec DEFLATE = new DeflateCompressionCodec();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Codec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> compression algorithm
|
||||||
|
*/
|
||||||
|
CompressionCodec GZIP = new GzipCompressionCodec();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of {@link CompressionCodecResolver}. This implementation will resolve DEF to
|
||||||
|
* {@link DeflateCompressionCodec} and GZIP to {@link GzipCompressionCodec}.
|
||||||
|
*
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
public class DefaultCompressionCodecResolver implements CompressionCodecResolver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompressionCodec resolveCompressionCodec(Header header) {
|
||||||
|
String cmpAlg = getAlgorithmFromHeader(header);
|
||||||
|
|
||||||
|
final boolean hasCompressionAlgorithm = Strings.hasText(cmpAlg);
|
||||||
|
if (!hasCompressionAlgorithm) {
|
||||||
|
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 + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAlgorithmFromHeader(Header header) {
|
||||||
|
Assert.notNull(header, "header cannot be null.");
|
||||||
|
|
||||||
|
return header.getCompressionAlgorithm();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Codec implementing the <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
public class DeflateCompressionCodec extends BaseCompressionCodec {
|
||||||
|
|
||||||
|
private static final String DEFLATE = "DEF";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithmName() {
|
||||||
|
return DEFLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] doCompress(byte[] payload) throws IOException {
|
||||||
|
|
||||||
|
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();
|
||||||
|
} finally {
|
||||||
|
Objects.nullSafeClose(outputStream, deflaterOutputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] doDecompress(byte[] compressed) throws IOException {
|
||||||
|
InflaterOutputStream inflaterOutputStream = null;
|
||||||
|
ByteArrayOutputStream decompressedOutputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
decompressedOutputStream = new ByteArrayOutputStream();
|
||||||
|
inflaterOutputStream = new InflaterOutputStream(decompressedOutputStream);
|
||||||
|
inflaterOutputStream.write(compressed);
|
||||||
|
inflaterOutputStream.flush();
|
||||||
|
return decompressedOutputStream.toByteArray();
|
||||||
|
} finally {
|
||||||
|
Objects.nullSafeClose(decompressedOutputStream, inflaterOutputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* 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.lang.Objects;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Codec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> compression algorithm
|
||||||
|
*
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
public class GzipCompressionCodec extends BaseCompressionCodec implements CompressionCodec {
|
||||||
|
|
||||||
|
private static final String GZIP = "GZIP";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAlgorithmName() {
|
||||||
|
return GZIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doDecompress(byte[] compressed) throws IOException {
|
||||||
|
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();
|
||||||
|
} finally {
|
||||||
|
Objects.nullSafeClose(inputStream, gzipInputStream, outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected byte[] doCompress(byte[] payload) throws IOException {
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
GZIPOutputStream compressorOutputStream = new GZIPOutputStream(outputStream, true);
|
||||||
|
try {
|
||||||
|
compressorOutputStream.write(payload, 0, payload.length);
|
||||||
|
compressorOutputStream.finish();
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
} finally {
|
||||||
|
Objects.nullSafeClose(compressorOutputStream, outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.lang;
|
package io.jsonwebtoken.lang;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -905,4 +907,19 @@ public abstract class Objects {
|
||||||
return sb.toString();
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package io.jsonwebtoken.lang;
|
package io.jsonwebtoken.lang;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -40,6 +41,8 @@ public abstract class Strings {
|
||||||
|
|
||||||
private static final char EXTENSION_SEPARATOR = '.';
|
private static final char EXTENSION_SEPARATOR = '.';
|
||||||
|
|
||||||
|
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// General convenience methods for working with Strings
|
// General convenience methods for working with Strings
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,13 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import io.jsonwebtoken.impl.DefaultHeader
|
import io.jsonwebtoken.impl.DefaultHeader
|
||||||
import io.jsonwebtoken.impl.DefaultJwsHeader
|
import io.jsonwebtoken.impl.DefaultJwsHeader
|
||||||
import io.jsonwebtoken.impl.TextCodec
|
import io.jsonwebtoken.impl.TextCodec
|
||||||
|
import io.jsonwebtoken.impl.compression.CompressionCodecs
|
||||||
|
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
|
||||||
|
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
|
||||||
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
|
import io.jsonwebtoken.impl.crypto.EllipticCurveProvider
|
||||||
import io.jsonwebtoken.impl.crypto.MacProvider
|
import io.jsonwebtoken.impl.crypto.MacProvider
|
||||||
import io.jsonwebtoken.impl.crypto.RsaProvider
|
import io.jsonwebtoken.impl.crypto.RsaProvider
|
||||||
|
import io.jsonwebtoken.lang.Strings
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
|
@ -108,6 +112,7 @@ class JwtsTest {
|
||||||
|
|
||||||
def token = Jwts.parser().parse(jwt);
|
def token = Jwts.parser().parse(jwt);
|
||||||
|
|
||||||
|
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||||
assert token.body == claims
|
assert token.body == claims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +191,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
|
@Test
|
||||||
void testConvenienceIssuer() {
|
void testConvenienceIssuer() {
|
||||||
String compact = Jwts.builder().setIssuer("Me").compact();
|
String compact = Jwts.builder().setIssuer("Me").compact();
|
||||||
|
@ -320,6 +335,140 @@ class JwtsTest {
|
||||||
assertNull claims.getId()
|
assertNull claims.getId()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testUncompressedJwt() {
|
||||||
|
|
||||||
|
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").compact()
|
||||||
|
|
||||||
|
def jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||||
|
|
||||||
|
Claims claims = jws.body
|
||||||
|
|
||||||
|
assertNull jws.header.getCompressionAlgorithm()
|
||||||
|
|
||||||
|
assertEquals id, claims.getId()
|
||||||
|
assertEquals "an audience", claims.getAudience()
|
||||||
|
assertEquals "hello this is an amazing jwt", claims.state
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 testCompressedWithCustomResolver() {
|
||||||
|
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(new GzipCompressionCodec() {
|
||||||
|
@Override
|
||||||
|
String getAlgorithmName() {
|
||||||
|
return "CUSTOM"
|
||||||
|
}
|
||||||
|
}).compact()
|
||||||
|
|
||||||
|
def jws = Jwts.parser().setSigningKey(key).setCompressionCodecResolver(new DefaultCompressionCodecResolver() {
|
||||||
|
@Override
|
||||||
|
CompressionCodec resolveCompressionCodec(Header header) {
|
||||||
|
String algorithm = header.getCompressionAlgorithm()
|
||||||
|
if ("CUSTOM".equals(algorithm)) {
|
||||||
|
return CompressionCodecs.GZIP
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).parseClaimsJws(compact)
|
||||||
|
|
||||||
|
Claims claims = jws.body
|
||||||
|
|
||||||
|
assertEquals "CUSTOM", jws.header.getCompressionAlgorithm()
|
||||||
|
|
||||||
|
assertEquals id, claims.getId()
|
||||||
|
assertEquals "an audience", claims.getAudience()
|
||||||
|
assertEquals "hello this is an amazing jwt", claims.state
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test(expected = CompressionException.class)
|
||||||
|
void testCompressedJwtWithUnrecognizedHeader() {
|
||||||
|
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(new GzipCompressionCodec() {
|
||||||
|
@Override
|
||||||
|
String getAlgorithmName() {
|
||||||
|
return "CUSTOM"
|
||||||
|
}
|
||||||
|
}).compact()
|
||||||
|
|
||||||
|
Jwts.parser().setSigningKey(key).parseClaimsJws(compact)
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
void testHS256() {
|
void testHS256() {
|
||||||
testHmac(SignatureAlgorithm.HS256);
|
testHmac(SignatureAlgorithm.HS256);
|
||||||
|
@ -571,6 +720,7 @@ class JwtsTest {
|
||||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||||
|
|
||||||
assert [alg: alg.name()] == token.header
|
assert [alg: alg.name()] == token.header
|
||||||
|
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||||
assert token.body == claims
|
assert token.body == claims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +735,7 @@ class JwtsTest {
|
||||||
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
def token = Jwts.parser().setSigningKey(key).parse(jwt)
|
||||||
|
|
||||||
assert token.header == [alg: alg.name()]
|
assert token.header == [alg: alg.name()]
|
||||||
|
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||||
assert token.body == claims
|
assert token.body == claims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,6 +757,7 @@ class JwtsTest {
|
||||||
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
def token = Jwts.parser().setSigningKey(key).parse(jwt);
|
||||||
|
|
||||||
assert token.header == [alg: alg.name()]
|
assert token.header == [alg: alg.name()]
|
||||||
|
//noinspection GrEqualsBetweenInconvertibleTypes
|
||||||
assert token.body == claims
|
assert token.body == claims
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ class DefaultJwtBuilderTest {
|
||||||
|
|
||||||
def b = new DefaultJwtBuilder() {
|
def b = new DefaultJwtBuilder() {
|
||||||
@Override
|
@Override
|
||||||
protected String toJson(Object o) throws JsonProcessingException {
|
protected byte[] toJson(Object o) throws JsonProcessingException {
|
||||||
throw new JsonMappingException('foo')
|
throw new JsonMappingException('foo')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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 org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.5.2
|
||||||
|
*/
|
||||||
|
class BaseCompressionCodecTest {
|
||||||
|
static class ExceptionThrowingCodec extends BaseCompressionCodec {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doCompress(byte[] payload) throws IOException {
|
||||||
|
throw new IOException("Test Exception")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String getAlgorithmName() {
|
||||||
|
return "Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] doDecompress(byte[] payload) throws IOException {
|
||||||
|
throw new IOException("Test Decompress Exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CompressionException.class)
|
||||||
|
void testCompressWithException() {
|
||||||
|
CompressionCodec codecUT = new ExceptionThrowingCodec();
|
||||||
|
codecUT.compress(new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CompressionException.class)
|
||||||
|
void testDecompressWithException() {
|
||||||
|
CompressionCodec codecUT = new ExceptionThrowingCodec();
|
||||||
|
codecUT.decompress(new byte[0]);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue