mirror of https://github.com/jwtk/jjwt.git
Issue-52 Adding ability to compress/decompress. Added tests for happy path.
This commit is contained in:
parent
56fb2c6a75
commit
19f6fcaa51
|
@ -1,4 +1,5 @@
|
|||
*.class
|
||||
.DS_Store
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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> */
|
||||
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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 ?
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
||||
}
|
|
@ -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 + "'");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
//---------------------------------------------------------------------
|
||||
|
|
|
@ -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,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);
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue