Skip to content

Commit

Permalink
Replace String/byte[] with (N)IO streams (#838)
Browse files Browse the repository at this point in the history
Closes #837 

Replaced raw `String` and `byte[]` usages with `CharSequence`, `InputStream`/`OutputStream` and `CharBuffer`/`ByteBuffer` concepts where possible to eliminate unnecessary creation of intermediate byte arrays and/or temporary Strings.

-----
- Changed TokenizedJwt and TokenizedJwe interfaces and implementations to return CharSequences instead of Strings to avoid creating new Strings on the heap
- Changed internal Base64 implementation to work with a CharSequence instead of a raw char[] to reduce need to create new arrays on the heap
- Changed Base64Decoder generics signature from Decoder<String,byte[]> to Decoder<CharSequence,byte[]>
- Decoders.BASE64 and Decoders.BASE64URL now reflect Decoder<CharSequence,byte[]>
- Changed Strings#utf8 implementation to accept a CharSequence instead of a String
- Added new Strings#wrap to wrap a CharSequence into a CharBuffer if necessary
- Renamed not-yet-released JwtBuilder#serializer method with JwtBuilder#json
- Renamed not-yet-released JwtParserBuilder#deserializer method with JwtParserBuilder#json

-----
- Moved JwtDeserializer from io.jsonwebtoken.impl to io.jsonwebtoken.impl.io package, created two new subclass implementations for use with Jwks and JwkSets
- Renamed JwtDeserializer to JsonObjectDeserializer that defaults to throwing MalformedJwtException.  Added two subclasses, JwkDeserializer and JwkSetDeserializer that throws JWK and JWK Set-specific exceptions.

-----
Changed ParserBuilder#deserializer method name to ParserBuilder#jsonReader

-----
Removed all usages of Serializer#serialize and Deserializer#deserialize except for deprecated implementations.  All other usages now use InputStream/OutputStream concepts

-----
Added Jwks#json and Jwks#UNSAFE_JSON for assistance in serializing JWKs to JSON (test cases, README examples, etc)

-----
- Ensured Encoder and CompressionAlgorithm supported streams instead of just byte arrays
- Copied over necessary (Apache-licensed) code from Apache commons-codec to obtain Base64OutputStream and Base64InputStream capability for efficient encoding during compact JWT creation.  Hopefully this is temporary and we can strip out most if not all of this and modify our existing Base64.java class for simpler support since we have many less use cases than what commons-codec supports.  All implementations are now in the `impl` module only.

-----
Converted all DigestAlgorithms to utilize an InputStream for data instead of byte[]

-----
- Added JwtBuilder InputStream payload support: added JwtBuilder#content(InputStream), JwtBuilder#content(InputStream, String contentType), JwtBuilder#content(String, String contentType)
- Added CountingInputStream as a way to check and assert that b64/unencoded payload InputStreams cannot be empty.

------
Renamed Encoder/Decoder and CompressionAlgorithm 'wrap' methods to encode/decode/compress/decompress for better readability and to make clearer the intent of the method. Also to avoid name/text/search collisions with 'wrap' references.

-----
Renamed new JwtBuilder#encoder and JwtParserBuilder#decoder methods to JwtBuilder#b64Url and JwtParserBuilder#b64Url for shorter method chains

-----
- Updated AeadAlgorithm and its AeadRequest/AeadResult concepts to utilize Input/Output Streams
- Renamed InitializationVectorSupplier to IvSupplier (was verbose, and it's a new interface, and it's not commonly referenced in the API, so the extra verbosity isn't needed)
  • Loading branch information
lhazlewood committed Sep 27, 2023
1 parent 7fcd652 commit b687ca5
Show file tree
Hide file tree
Showing 179 changed files with 7,267 additions and 1,955 deletions.
36 changes: 36 additions & 0 deletions NOTICE.md
Expand Up @@ -33,3 +33,39 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWIS
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
```

Additionally, the following classes were copied from the Apache Commons-Codec project, with further JJWT-specific
modifications:
* io.jsonwebtoken.impl.io.Base64Codec
* io.jsonwebtoken.impl.io.Base64InputStream
* io.jsonwebtoken.impl.io.Base64OutputStream
* io.jsonwebtoken.impl.io.BaseNCodec
* io.jsonwebtoken.impl.io.BaseNCodecInputStream
* io.jsonwebtoken.impl.io.BaseNCodecOutputStream
* io.jsonwebtoken.impl.io.CodecPolicy

Its attribution:

```
Apache Commons Codec
Copyright 2002-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
```

Also, the following classes were copied from the Apache Commons-IO project, with further JJWT-specific modifications:
* io.jsonwebtoken.impl.io.FilteredInputStream
* io.jsonwebtoken.impl.io.FilteredOutputStream
* io.jsonwebtoken.impl.io.ClosedInputStream
* io.jsonwebtoken.impl.io.UncloseableInputStream

It's attribution:

```
Apache Commons IO
Copyright 2002-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (https://www.apache.org/).
```
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -164,7 +164,7 @@ JJWT is open source under the terms of the [Apache 2.0 License](http://www.apach
* Convenient and readable [fluent](http://en.wikipedia.org/wiki/Fluent_interface) interfaces, great for IDE
auto-completion to write code quickly
* Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors
* Stable implementation with over 1,500+ tests and enforced 100% test code coverage. Every single method, statement
* Stable implementation with almost 1,700 tests and enforced 100% test code coverage. Every single method, statement
and conditional branch variant in the entire codebase is tested and required to pass on every build.
* Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms:

Expand Down
13 changes: 7 additions & 6 deletions api/src/main/java/io/jsonwebtoken/Claims.java
Expand Up @@ -144,14 +144,14 @@ public interface Claims extends Map<String, Object>, Identifiable {
String getId();

/**
* Returns the JWTs claim ({@code claimName}) value as a type {@code requiredType}, or {@code null} if not present.
* Returns the JWTs claim ({@code claimName}) value as a {@code requiredType} instance, or {@code null} if not
* present.
*
* <p>JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. Anything more
* complex is expected to be already converted to your desired type by the JSON
* {@link io.jsonwebtoken.io.Deserializer Deserializer} implementation. You may specify a custom Deserializer for a
* JwtParser with the desired conversion configuration via the
* {@link JwtParserBuilder#deserializer deserializer} method.
* See <a href="https://github.com/jwtk/jjwt#custom-json-processor">custom JSON processor</a> for more
* complex is expected to be already converted to your desired type by the JSON parser. You may specify a custom
* JSON processor using the {@code JwtParserBuilder}'s
* {@link JwtParserBuilder#json(io.jsonwebtoken.io.Deserializer) json(Deserializer)} method. See the JJWT
* documentation on <a href="https://github.com/jwtk/jjwt#custom-json-processor">custom JSON processor</a>s for more
* information. If using Jackson, you can specify custom claim POJO types as described in
* <a href="https://github.com/jwtk/jjwt#json-jackson-custom-types">custom claim types</a>.
*
Expand All @@ -160,6 +160,7 @@ public interface Claims extends Map<String, Object>, Identifiable {
* @param <T> the type of the value expected to be returned
* @return the JWT {@code claimName} value or {@code null} if not present.
* @throws RequiredTypeException throw if the claim value is not null and not of type {@code requiredType}
* @see <a href="https://github.com/jwtk/jjwt#json-support">JJWT JSON Support</a>
*/
<T> T get(String claimName, Class<T> requiredType);
}
32 changes: 19 additions & 13 deletions api/src/main/java/io/jsonwebtoken/ClaimsMutator.java
Expand Up @@ -30,7 +30,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1">
* <code>iss</code></a> (issuer) value. A {@code null} value will remove the property from the JSON map.
* <code>iss</code></a> (issuer) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* @param iss the JWT {@code iss} value or {@code null} to remove the property from the JSON map.
* @return the {@code Claims} instance for method chaining.
Expand All @@ -42,7 +42,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1">
* <code>iss</code></a> (issuer) value. A {@code null} value will remove the property from the JSON map.
* <code>iss</code></a> (issuer) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* @param iss the JWT {@code iss} value or {@code null} to remove the property from the JSON map.
* @return the {@code Claims} instance for method chaining.
Expand All @@ -52,7 +52,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2">
* <code>sub</code></a> (subject) value. A {@code null} value will remove the property from the JSON map.
* <code>sub</code></a> (subject) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* @param sub the JWT {@code sub} value or {@code null} to remove the property from the JSON map.
* @return the {@code Claims} instance for method chaining.
Expand All @@ -64,7 +64,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2">
* <code>sub</code></a> (subject) value. A {@code null} value will remove the property from the JSON map.
* <code>sub</code></a> (subject) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* @param sub the JWT {@code sub} value or {@code null} to remove the property from the JSON map.
* @return the {@code Claims} instance for method chaining.
Expand All @@ -74,7 +74,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3"><code>aud</code> (audience)
* Claim</a> as <em>a single String, <b>NOT</b> a String array</em>. This method exists only for producing
* claim</a> as <em>a single String, <b>NOT</b> a String array</em>. This method exists only for producing
* JWTs sent to legacy recipients that are unable to interpret the {@code aud} value as a JSON String Array; it is
* strongly recommended to avoid calling this method whenever possible and favor the
* {@link #audience(String)} or {@link #audience(Collection)} methods instead, as they ensure a single deterministic
Expand Down Expand Up @@ -135,7 +135,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4">
* <code>exp</code></a> (expiration) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>exp</code></a> (expiration) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>A JWT obtained after this timestamp should not be used.</p>
*
Expand All @@ -149,7 +150,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4">
* <code>exp</code></a> (expiration) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>exp</code></a> (expiration) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>A JWT obtained after this timestamp should not be used.</p>
*
Expand All @@ -161,7 +163,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5">
* <code>nbf</code></a> (not before) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>nbf</code></a> (not before) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>A JWT obtained before this timestamp should not be used.</p>
*
Expand All @@ -175,7 +178,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5">
* <code>nbf</code></a> (not before) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>nbf</code></a> (not before) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>A JWT obtained before this timestamp should not be used.</p>
*
Expand All @@ -187,7 +191,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6">
* <code>iat</code></a> (issued at) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>iat</code></a> (issued at) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>The value is the timestamp when the JWT was created.</p>
*
Expand All @@ -201,7 +206,8 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6">
* <code>iat</code></a> (issued at) timestamp. A {@code null} value will remove the property from the JSON map.
* <code>iat</code></a> (issued at) timestamp claim. A {@code null} value will remove the property from the
* JSON Claims map.
*
* <p>The value is the timestamp when the JWT was created.</p>
*
Expand All @@ -213,7 +219,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7">
* <code>jti</code></a> (JWT ID) value. A {@code null} value will remove the property from the JSON map.
* <code>jti</code></a> (JWT ID) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a
* manner that ensures that there is a negligible probability that the same value will be accidentally
Expand All @@ -229,7 +235,7 @@ public interface ClaimsMutator<T extends ClaimsMutator<T>> {

/**
* Sets the JWT <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7">
* <code>jti</code></a> (JWT ID) value. A {@code null} value will remove the property from the JSON map.
* <code>jti</code></a> (JWT ID) claim. A {@code null} value will remove the property from the JSON Claims map.
*
* <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a
* manner that ensures that there is a negligible probability that the same value will be accidentally
Expand Down
23 changes: 22 additions & 1 deletion api/src/main/java/io/jsonwebtoken/CompressionCodec.java
Expand Up @@ -46,4 +46,25 @@ public interface CompressionCodec extends CompressionAlgorithm {
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
String getAlgorithmName();
}

/**
* Compresses the specified byte array, returning the compressed byte array result.
*
* @param content bytes to compress
* @return compressed bytes
* @throws CompressionException if the specified byte array cannot be compressed.
*/
@Deprecated
byte[] compress(byte[] content) throws CompressionException;

/**
* Decompresses the specified compressed byte array, returning the decompressed byte array result. The
* specified byte array must already be in compressed form.
*
* @param compressed compressed bytes
* @return decompressed bytes
* @throws CompressionException if the specified byte array cannot be decompressed.
*/
@Deprecated
byte[] decompress(byte[] compressed) throws CompressionException;
}

0 comments on commit b687ca5

Please sign in to comment.