diff --git a/NOTICE.md b/NOTICE.md
index 079325e4..fbfc898f 100644
--- a/NOTICE.md
+++ b/NOTICE.md
@@ -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
diff --git a/api/src/main/java/io/jsonwebtoken/JwtParser.java b/api/src/main/java/io/jsonwebtoken/JwtParser.java
index b2888064..e0da4cdb 100644
--- a/api/src/main/java/io/jsonwebtoken/JwtParser.java
+++ b/api/src/main/java/io/jsonwebtoken/JwtParser.java
@@ -15,6 +15,7 @@
*/
package io.jsonwebtoken;
+import io.jsonwebtoken.io.Parser;
import io.jsonwebtoken.security.SecurityException;
import io.jsonwebtoken.security.SignatureException;
@@ -25,7 +26,7 @@ import java.io.InputStream;
*
* @since 0.1
*/
-public interface JwtParser {
+public interface JwtParser extends Parser> {
/**
* Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
@@ -38,7 +39,7 @@ 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
@@ -46,7 +47,7 @@ public interface JwtParser {
*
*
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.
*
* @param jwt the compact serialized JWT to parse
@@ -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;
/**
@@ -93,12 +94,12 @@ public interface JwtParser {
* following convenience methods instead of this one:
*
*
- *
{@link #parseContentJwt(String)}
- *
{@link #parseClaimsJwt(String)}
- *
{@link #parseContentJws(String)}
- *
{@link #parseClaimsJws(String)}
- *
{@link #parseContentJwe(String)}
- *
{@link #parseClaimsJwe(String)}
+ *
{@link #parseContentJwt(CharSequence)}
+ *
{@link #parseClaimsJwt(CharSequence)}
+ *
{@link #parseContentJws(CharSequence)}
+ *
{@link #parseClaimsJws(CharSequence)}
+ *
{@link #parseContentJwe(CharSequence)}
+ *
{@link #parseClaimsJwe(CharSequence)}
*
*
* @param jwt the compact serialized JWT to parse
@@ -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 parse(String jwt, JwtHandler handler) throws ExpiredJwtException, UnsupportedJwtException,
+ T parse(CharSequence jwt, JwtHandler handler) throws ExpiredJwtException, UnsupportedJwtException,
MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;
/**
@@ -148,14 +149,14 @@ public interface JwtParser {
* @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 parseContentJwt(String jwt) throws UnsupportedJwtException, MalformedJwtException,
+ Jwt parseContentJwt(CharSequence jwt) throws UnsupportedJwtException, MalformedJwtException,
SignatureException, SecurityException, IllegalArgumentException;
/**
@@ -178,14 +179,14 @@ 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 {@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 parseClaimsJwt(String jwt) throws ExpiredJwtException, UnsupportedJwtException,
+ Jwt parseClaimsJwt(CharSequence jwt) throws ExpiredJwtException, UnsupportedJwtException,
MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;
/**
@@ -207,16 +208,16 @@ public interface JwtParser {
* @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 parseContentJws(String jws) throws UnsupportedJwtException, MalformedJwtException, SignatureException,
+ Jws parseContentJws(CharSequence jws) throws UnsupportedJwtException, MalformedJwtException, SignatureException,
SecurityException, IllegalArgumentException;
/**
@@ -234,7 +235,7 @@ public interface JwtParser {
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
- Jws parseContentJws(String jws, byte[] unencodedPayload);
+ Jws parseContentJws(CharSequence jws, byte[] unencodedPayload);
/**
* Parses a JWS known to use the
@@ -251,7 +252,7 @@ public interface JwtParser {
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
- Jws parseClaimsJws(String jws, byte[] unencodedPayload);
+ Jws parseClaimsJws(CharSequence jws, byte[] unencodedPayload);
/**
* Parses a JWS known to use the
@@ -274,7 +275,7 @@ public interface JwtParser {
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
- Jws parseContentJws(String jws, InputStream unencodedPayload);
+ Jws parseContentJws(CharSequence jws, InputStream unencodedPayload);
/**
* Parses a JWS known to use the
@@ -285,7 +286,7 @@ public interface JwtParser {
*
NOTE: 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.
+ * large Claims), it is recommended to use the {@link #parseContentJws(CharSequence, InputStream)} method instead.
*
*
Unencoded Non-Detached Payload
*
Note that if the JWS contains a valid unencoded Payload string (what RFC 7797 calls an
@@ -297,7 +298,7 @@ public interface JwtParser {
* @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.
* @return the parsed Unencoded Payload.
*/
- Jws parseClaimsJws(String jws, InputStream unencodedPayload);
+ Jws parseClaimsJws(CharSequence jws, InputStream unencodedPayload);
/**
* Parses the specified compact serialized JWS string based on the builder's current configuration state and
@@ -318,16 +319,16 @@ 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 {@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 parseClaimsJws(String jws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
+ Jws parseClaimsJws(CharSequence jws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SignatureException, SecurityException, IllegalArgumentException;
/**
@@ -348,16 +349,16 @@ public interface JwtParser {
* @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 parseContentJwe(String jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
+ Jwe parseContentJwe(CharSequence jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SecurityException, IllegalArgumentException;
/**
@@ -378,15 +379,15 @@ public interface JwtParser {
* @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 parseClaimsJwe(String jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
+ Jwe parseClaimsJwe(CharSequence jwe) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SecurityException, IllegalArgumentException;
}
diff --git a/api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java b/api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java
index a9a946e8..10526eaf 100644
--- a/api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java
+++ b/api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java
@@ -19,10 +19,13 @@ import io.jsonwebtoken.lang.Assert;
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 the type of object returned after deserialization
* @since JJWT_RELEASE_VERSION
@@ -48,17 +51,19 @@ public abstract class AbstractDeserializer implements Deserializer {
@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;
@@ -69,11 +74,11 @@ public abstract class AbstractDeserializer implements Deserializer {
}
/**
- * 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;
}
diff --git a/api/src/main/java/io/jsonwebtoken/io/Deserializer.java b/api/src/main/java/io/jsonwebtoken/io/Deserializer.java
index 5b73b5e1..4962425e 100644
--- a/api/src/main/java/io/jsonwebtoken/io/Deserializer.java
+++ b/api/src/main/java/io/jsonwebtoken/io/Deserializer.java
@@ -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.
@@ -31,18 +31,18 @@ public interface Deserializer {
* @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;
}
diff --git a/api/src/main/java/io/jsonwebtoken/io/Parser.java b/api/src/main/java/io/jsonwebtoken/io/Parser.java
index 4addf0a1..0f5c3180 100644
--- a/api/src/main/java/io/jsonwebtoken/io/Parser.java
+++ b/api/src/main/java/io/jsonwebtoken/io/Parser.java
@@ -15,32 +15,53 @@
*/
package io.jsonwebtoken.io;
+import java.io.InputStream;
+import java.io.Reader;
+
/**
- * A Parser converts character input into a Java object.
+ * A Parser converts a character stream into a Java object.
*
- *
Semantically, this interface might have been more accurately named
- * Unmarshaller because it technically
- * converts a content stream into a Java object. However, the {@code Parser} name was chosen for consistency with the
- * {@link io.jsonwebtoken.JwtParser JwtParser} concept (which is a 'real' parser that scans text for tokens). This
- * helps avoid confusion when trying to find similar concepts in the JJWT API by using the same taxonomy, for
- * example:
- *
- * @param the instance type created after parsing/unmarshalling
+ * @param the instance type created after parsing
* @since JJWT_RELEASE_VERSION
*/
public interface Parser {
/**
- * Parse the specified input into a Java object.
+ * Parse the specified character sequence into a Java object.
*
- * @param input the string to parse into a Java object.
+ * @param input the character sequence to parse into a Java object.
* @return the Java object represented by the specified {@code input} stream.
*/
- T parse(String input);
+ T parse(CharSequence input);
+ /**
+ * @param input The character sequence, may be {@code null}
+ * @param start The start index in the character sequence, inclusive
+ * @param end The end index in the character sequence, exclusive
+ * @return the Java object represented by the specified sequence bounds
+ * @throws IllegalArgumentException if the start index is negative, or if the end index is smaller than the start index
+ */
+ T parse(CharSequence input, int start, int end);
+
+ /**
+ * Parse the specified character sequence into a Java object.
+ *
+ * @param reader the reader to use to parse a Java object.
+ * @return the Java object represented by the specified {@code input} stream.
+ */
+ T parse(Reader reader);
+
+ /**
+ * Parses the specified {@link InputStream} assuming {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} encoding.
+ * This is a convenience alias for:
+ *
+ *