Skip to content

Commit

Permalink
Expanded Parser method argument support (#844)
Browse files Browse the repository at this point in the history
Closes #328

- Ensured Parser worked with CharSequence, InputStream and Reader, not just String
- Changed Deserializer#deserialize(InputStream) to deserialize(Reader) 
- JwtParser now extends from Parser to support these additional methods.
- Changed remaining JwtParser.parse* methods to accept CharSequence arguments instead of String args.
  • Loading branch information
lhazlewood committed Sep 30, 2023
1 parent 36a6e13 commit 05717d0
Show file tree
Hide file tree
Showing 31 changed files with 698 additions and 252 deletions.
1 change: 1 addition & 0 deletions NOTICE.md
Expand Up @@ -55,6 +55,7 @@ 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.CharSequenceReader
* io.jsonwebtoken.impl.io.FilteredInputStream
* io.jsonwebtoken.impl.io.FilteredOutputStream
* io.jsonwebtoken.impl.io.ClosedInputStream
Expand Down
149 changes: 75 additions & 74 deletions api/src/main/java/io/jsonwebtoken/JwtParser.java
Expand Up @@ -15,6 +15,7 @@
*/
package io.jsonwebtoken;

import io.jsonwebtoken.io.Parser;
import io.jsonwebtoken.security.SecurityException;
import io.jsonwebtoken.security.SignatureException;

Expand All @@ -25,7 +26,7 @@
*
* @since 0.1
*/
public interface JwtParser {
public interface JwtParser extends Parser<Jwt<?, ?>> {

/**
* Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
Expand All @@ -38,15 +39,15 @@ public interface JwtParser {
* @return {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
* otherwise.
*/
boolean isSigned(String compact);
boolean isSigned(CharSequence compact);

/**
* Parses the specified compact serialized JWT string based on the builder's current configuration state and
* returns the resulting JWT, JWS, or JWE instance.
*
* <p>This method returns a JWT, JWS, or JWE based on the parsed string. Because it may be cumbersome to
* determine if it is a JWT, JWS or JWE, or if the payload is a Claims or byte array with {@code instanceof} checks,
* the {@link #parse(String, JwtHandler) parse(String,JwtHandler)} method allows for a type-safe callback approach
* the {@link #parse(CharSequence, JwtHandler) parse(String,JwtHandler)} method allows for a type-safe callback approach
* that may help reduce code or instanceof checks.</p>
*
* @param jwt the compact serialized JWT to parse
Expand All @@ -59,15 +60,15 @@ public interface JwtParser {
* @throws ExpiredJwtException if the specified JWT is a Claims JWT and the Claims has an expiration time
* before the time this method is invoked.
* @throws IllegalArgumentException if the specified string is {@code null} or empty or only whitespace.
* @see #parse(String, JwtHandler)
* @see #parseContentJwt(String)
* @see #parseClaimsJwt(String)
* @see #parseContentJws(String)
* @see #parseClaimsJws(String)
* @see #parseContentJwe(String)
* @see #parseClaimsJwe(String)
* @see #parse(CharSequence, JwtHandler)
* @see #parseContentJwt(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parseContentJwe(CharSequence)
* @see #parseClaimsJwe(CharSequence)
*/
Jwt<?, ?> parse(String jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException,
Jwt<?, ?> parse(CharSequence jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException,
SecurityException, IllegalArgumentException;

/**
Expand All @@ -93,12 +94,12 @@ public interface JwtParser {
* following convenience methods instead of this one:</p>
*
* <ul>
* <li>{@link #parseContentJwt(String)}</li>
* <li>{@link #parseClaimsJwt(String)}</li>
* <li>{@link #parseContentJws(String)}</li>
* <li>{@link #parseClaimsJws(String)}</li>
* <li>{@link #parseContentJwe(String)}</li>
* <li>{@link #parseClaimsJwe(String)}</li>
* <li>{@link #parseContentJwt(CharSequence)}</li>
* <li>{@link #parseClaimsJwt(CharSequence)}</li>
* <li>{@link #parseContentJws(CharSequence)}</li>
* <li>{@link #parseClaimsJws(CharSequence)}</li>
* <li>{@link #parseContentJwe(CharSequence)}</li>
* <li>{@link #parseClaimsJwe(CharSequence)}</li>
* </ul>
*
* @param jwt the compact serialized JWT to parse
Expand All @@ -114,16 +115,16 @@ public interface JwtParser {
* before the time this method is invoked.
* @throws IllegalArgumentException if the specified string is {@code null} or empty or only whitespace, or if the
* {@code handler} is {@code null}.
* @see #parseContentJwt(String)
* @see #parseClaimsJwt(String)
* @see #parseContentJws(String)
* @see #parseClaimsJws(String)
* @see #parseContentJwe(String)
* @see #parseClaimsJwe(String)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parseContentJwe(CharSequence)
* @see #parseClaimsJwe(CharSequence)
* @see #parse(CharSequence)
* @since 0.2
*/
<T> T parse(String jwt, JwtHandler<T> handler) throws ExpiredJwtException, UnsupportedJwtException,
<T> T parse(CharSequence jwt, JwtHandler<T> handler) throws ExpiredJwtException, UnsupportedJwtException,
MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;

/**
Expand All @@ -148,14 +149,14 @@ <T> T parse(String jwt, JwtHandler<T> handler) throws ExpiredJwtException, Unsup
* @throws SignatureException if the {@code jwt} string is actually a JWS and signature validation fails
* @throws SecurityException if the {@code jwt} string is actually a JWE and decryption fails
* @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace
* @see #parseClaimsJwt(String)
* @see #parseContentJws(String)
* @see #parseClaimsJws(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseClaimsJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since 0.2
*/
Jwt<Header, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException,
Jwt<Header, byte[]> parseContentJwt(CharSequence jwt) throws UnsupportedJwtException, MalformedJwtException,
SignatureException, SecurityException, IllegalArgumentException;

/**
Expand All @@ -178,14 +179,14 @@ Jwt<Header, byte[]> parseContentJwt(String jwt) throws UnsupportedJwtException,
* @throws ExpiredJwtException if the specified JWT is a Claims JWT and the Claims has an expiration time
* before the time this method is invoked.
* @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace
* @see #parseContentJwt(String)
* @see #parseContentJws(String)
* @see #parseClaimsJws(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since 0.2
*/
Jwt<Header, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException,
Jwt<Header, Claims> parseClaimsJwt(CharSequence jwt) throws ExpiredJwtException, UnsupportedJwtException,
MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;

/**
Expand All @@ -207,16 +208,16 @@ Jwt<Header, Claims> parseClaimsJwt(String jwt) throws ExpiredJwtException, Unsup
* @throws SignatureException if the {@code jws} JWS signature validation fails
* @throws SecurityException if the {@code jws} string is actually a JWE and decryption fails
* @throws IllegalArgumentException if the {@code jws} string is {@code null} or empty or only whitespace
* @see #parseContentJwt(String)
* @see #parseContentJwe(String)
* @see #parseClaimsJwt(String)
* @see #parseClaimsJws(String)
* @see #parseClaimsJwe(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseContentJwe(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parseClaimsJwe(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since 0.2
*/
Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, MalformedJwtException, SignatureException,
Jws<byte[]> parseContentJws(CharSequence jws) throws UnsupportedJwtException, MalformedJwtException, SignatureException,
SecurityException, IllegalArgumentException;

/**
Expand All @@ -234,7 +235,7 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
Jws<byte[]> parseContentJws(String jws, byte[] unencodedPayload);
Jws<byte[]> parseContentJws(CharSequence jws, byte[] unencodedPayload);

/**
* Parses a JWS known to use the
Expand All @@ -251,7 +252,7 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
Jws<Claims> parseClaimsJws(String jws, byte[] unencodedPayload);
Jws<Claims> parseClaimsJws(CharSequence jws, byte[] unencodedPayload);

/**
* Parses a JWS known to use the
Expand All @@ -274,7 +275,7 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
Jws<byte[]> parseContentJws(String jws, InputStream unencodedPayload);
Jws<byte[]> parseContentJws(CharSequence jws, InputStream unencodedPayload);

/**
* Parses a JWS known to use the
Expand All @@ -285,7 +286,7 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* <p><b>NOTE:</b> however, because calling this method indicates a completed
* {@link Claims} instance is desired, the specified {@code unencodedPayload} JSON stream will be fully
* read into a Claims instance. If this will be problematic for your application (perhaps if you expect extremely
* large Claims), it is recommended to use the {@link #parseContentJws(String, InputStream)} method instead.</p>
* large Claims), it is recommended to use the {@link #parseContentJws(CharSequence, InputStream)} method instead.</p>
*
* <p><b>Unencoded Non-Detached Payload</b></p>
* <p>Note that if the JWS contains a valid unencoded Payload string (what RFC 7797 calls an
Expand All @@ -297,7 +298,7 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
Jws<Claims> parseClaimsJws(String jws, InputStream unencodedPayload);
Jws<Claims> parseClaimsJws(CharSequence jws, InputStream unencodedPayload);

/**
* Parses the specified compact serialized JWS string based on the builder's current configuration state and
Expand All @@ -318,16 +319,16 @@ Jws<byte[]> parseContentJws(String jws) throws UnsupportedJwtException, Malforme
* @throws ExpiredJwtException if the specified JWT is a Claims JWT and the Claims has an expiration time
* before the time this method is invoked.
* @throws IllegalArgumentException if the {@code claimsJws} string is {@code null} or empty or only whitespace
* @see #parseContentJwt(String)
* @see #parseContentJws(String)
* @see #parseContentJwe(String)
* @see #parseClaimsJwt(String)
* @see #parseClaimsJwe(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseContentJwe(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseClaimsJwe(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since 0.2
*/
Jws<Claims> parseClaimsJws(String jws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
Jws<Claims> parseClaimsJws(CharSequence jws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SignatureException, SecurityException, IllegalArgumentException;

/**
Expand All @@ -348,16 +349,16 @@ Jws<Claims> parseClaimsJws(String jws) throws ExpiredJwtException, UnsupportedJw
* @throws MalformedJwtException if the {@code jwe} string is not a valid JWE
* @throws SecurityException if the {@code jwe} JWE decryption fails
* @throws IllegalArgumentException if the {@code jwe} string is {@code null} or empty or only whitespace
* @see #parseContentJwt(String)
* @see #parseContentJws(String)
* @see #parseClaimsJwt(String)
* @see #parseClaimsJws(String)
* @see #parseClaimsJwe(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parseClaimsJwe(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since JJWT_RELEASE_VERSION
*/
Jwe<byte[]> parseContentJwe(String jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
Jwe<byte[]> parseContentJwe(CharSequence jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SecurityException, IllegalArgumentException;

/**
Expand All @@ -378,15 +379,15 @@ Jwe<byte[]> parseContentJwe(String jwe) throws ExpiredJwtException, UnsupportedJ
* @throws ExpiredJwtException if the specified JWT is a Claims JWE and the Claims has an expiration time
* before the time this method is invoked.
* @throws IllegalArgumentException if the {@code claimsJwe} string is {@code null} or empty or only whitespace
* @see #parseContentJwt(String)
* @see #parseContentJws(String)
* @see #parseContentJwe(String)
* @see #parseClaimsJwt(String)
* @see #parseClaimsJws(String)
* @see #parse(String, JwtHandler)
* @see #parse(String)
* @see #parseContentJwt(CharSequence)
* @see #parseContentJws(CharSequence)
* @see #parseContentJwe(CharSequence)
* @see #parseClaimsJwt(CharSequence)
* @see #parseClaimsJws(CharSequence)
* @see #parse(CharSequence, JwtHandler)
* @see #parse(CharSequence)
* @since JJWT_RELEASE_VERSION
*/
Jwe<Claims> parseClaimsJwe(String jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
Jwe<Claims> parseClaimsJwe(CharSequence jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SecurityException, IllegalArgumentException;
}
21 changes: 13 additions & 8 deletions api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java
Expand Up @@ -19,10 +19,13 @@

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;

/**
* Convenient base class to use to implement {@link Deserializer}s, with subclasses only needing to implement
* {@link #doDeserialize(InputStream)}.
* {@link #doDeserialize(Reader)}.
*
* @param <T> the type of object returned after deserialization
* @since JJWT_RELEASE_VERSION
Expand All @@ -48,17 +51,19 @@ protected AbstractDeserializer() {
@Override
public final T deserialize(byte[] bytes) throws DeserializationException {
bytes = bytes == null ? EMPTY_BYTES : bytes; // null safe
return deserialize(new ByteArrayInputStream(bytes));
InputStream in = new ByteArrayInputStream(bytes);
Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
return deserialize(reader);
}

/**
* {@inheritDoc}
*/
@Override
public final T deserialize(InputStream in) throws DeserializationException {
Assert.notNull(in, "InputStream argument cannot be null.");
public final T deserialize(Reader reader) throws DeserializationException {
Assert.notNull(reader, "Reader argument cannot be null.");
try {
return doDeserialize(in);
return doDeserialize(reader);
} catch (Throwable t) {
if (t instanceof DeserializationException) {
throw (DeserializationException) t;
Expand All @@ -69,11 +74,11 @@ public final T deserialize(InputStream in) throws DeserializationException {
}

/**
* Reads the specified {@code InputStream} and returns the corresponding Java object.
* Reads the specified character stream and returns the corresponding Java object.
*
* @param in the input stream to read
* @param reader the reader to use to read the character stream
* @return the deserialized Java object
* @throws Exception if there is a problem reading the stream or creating the expected Java object
*/
protected abstract T doDeserialize(InputStream in) throws Exception;
protected abstract T doDeserialize(Reader reader) throws Exception;
}
10 changes: 5 additions & 5 deletions api/src/main/java/io/jsonwebtoken/io/Deserializer.java
Expand Up @@ -15,7 +15,7 @@
*/
package io.jsonwebtoken.io;

import java.io.InputStream;
import java.io.Reader;

/**
* A {@code Deserializer} is able to convert serialized byte streams into Java objects.
Expand All @@ -31,18 +31,18 @@ public interface Deserializer<T> {
* @param bytes the formatted data byte array to convert
* @return the reconstituted Java object
* @throws DeserializationException if there is a problem converting the byte array to an object.
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #deserialize(InputStream)}
* @deprecated since JJWT_RELEASE_VERSION in favor of {@link #deserialize(Reader)}
*/
@Deprecated
T deserialize(byte[] bytes) throws DeserializationException;

/**
* Reads the specified {@code InputStream} and returns the corresponding Java object.
* Reads the specified character stream and returns the corresponding Java object.
*
* @param in the input stream to read
* @param reader the reader to use to read the character stream
* @return the deserialized Java object
* @throws DeserializationException if there is a problem reading the stream or creating the expected Java object
* @since JJWT_RELEASE_VERSION
*/
T deserialize(InputStream in) throws DeserializationException;
T deserialize(Reader reader) throws DeserializationException;
}

0 comments on commit 05717d0

Please sign in to comment.