Clean build on all available JDK versions (#729)

- POM and JavaDoc updates to get a clean (warning free) build, remove duplicates, etc
- Ensured CI uses the release build profile (i.e. 'ossrh') to ensure we can execute all things necessary for a release.  This will not deploy to ossrh however, as we do that manually during a release per https://github.com/jwtk/jjwt/wiki#release-instructions
- Fixing JavaDoc lint errors surfacing on JDK 14
- Enable html5 for JavaDoc on JDK >= 9
- Used version properties and Maven profiles to allow the japicmp-maven-plugin to work with JDK 7 through 18
- Minor CI job name fixes, added additional zulu JDK versions
- Fixed build to run on all JDKs, from 7 to 18 inclusive
This commit is contained in:
Les Hazlewood 2022-04-25 21:51:28 -04:00 committed by GitHub
parent b78473262d
commit 451c8d44dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 349 additions and 363 deletions

View File

@ -7,24 +7,27 @@ on:
- master
jobs:
hotspot:
temurin:
strategy:
matrix:
java: [ '8', '11' ]
java: [ '8', '11', '17', '18' ]
runs-on: 'ubuntu-latest'
env:
MVN_CMD: ./mvnw --no-transfer-progress -B
name: java-${{ matrix.java }}
name: jdk-${{ matrix.java }}-temurin
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v2.4.0
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
check-latest: true
- name: Build
run: ${{env.MVN_CMD}} verify
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
- name: Code coverage
if: matrix.java == '8'
run: |
@ -37,34 +40,40 @@ jobs:
zulu:
strategy:
matrix:
java: [ '9', '10', '14' ]
java: [ '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18' ]
runs-on: 'ubuntu-latest'
env:
MVN_CMD: ./mvnw --no-transfer-progress -B
name: java-${{ matrix.java }}
JDK_MAJOR_VERSION: ${{ matrix.java }}
name: jdk-${{ matrix.java }}-zulu
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v2.4.0
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'zulu'
cache: 'maven'
check-latest: true
- name: Build
run: ${{env.MVN_CMD}} verify
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
run: |
if [ "$JDK_MAJOR_VERSION" == "7" ]; then export MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=128m"; fi
${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true
java-7:
jdk-7-oracle:
runs-on: 'ubuntu-latest'
env:
MVN_CMD: ./mvnw --no-transfer-progress -B
MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Xmx512m -XX:MaxPermSize=128m"
steps:
- uses: actions/checkout@v2.4.0
- uses: actions/checkout@v3
- run: |
download_url="https://238dj3282as03k369.s3-us-west-1.amazonaws.com/jdk-7u80-linux-x64.tar.gz"
wget -O $RUNNER_TEMP/java_package.tar.gz $download_url
- name: Set up JDK
uses: actions/setup-java@v2.4.0
uses: actions/setup-java@v3
with:
distribution: 'jdkfile'
jdkFile: ${{ runner.temp }}/java_package.tar.gz
@ -77,4 +86,6 @@ jobs:
unzip -oj -d "$JAVA_HOME/jre/lib/security" $RUNNER_TEMP/UnlimitedJCEPolicyJDK7.zip \*/\*.jar
rm $RUNNER_TEMP/UnlimitedJCEPolicyJDK7.zip
- name: Build
run: ${{env.MVN_CMD}} verify
# run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg
# to sign artifacts, since we don't want to mess with storing signing credentials in CI:
run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true

View File

@ -31,7 +31,7 @@ import java.util.Map;
* claims.{@link Map#put(Object, Object) put}("someKey", "someValue");
* </pre>
*
* <h3>Creation</h3>
* <h2>Creation</h2>
*
* <p>It is easiest to create a {@code Claims} instance by calling one of the
* {@link Jwts#claims() JWTs.claims()} factory methods.</p>
@ -177,7 +177,7 @@ public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {
* 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#deserializeJsonWith} method.
* See <a href="https://github.com/jwtk/jjwt#custom-json-processor">custom JSON processor</a></a> for more
* See <a href="https://github.com/jwtk/jjwt#custom-json-processor">custom JSON processor</a> 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>.
*

View File

@ -34,16 +34,19 @@ public final class CompressionCodecs {
* <a href="https://en.wikipedia.org/wiki/DEFLATE">deflate</a> compression algorithm
*/
public static final CompressionCodec DEFLATE =
Classes.newInstance("io.jsonwebtoken.impl.compression.DeflateCompressionCodec");
Classes.newInstance("io.jsonwebtoken.impl.compression.DeflateCompressionCodec");
/**
* Codec implementing the <a href="https://en.wikipedia.org/wiki/Gzip">gzip</a> compression algorithm.
* <h3>Compatibility Warning</h3>
*
* <p><b>Compatibility Warning</b></p>
*
* <p><b>This is not a standard JWA compression algorithm</b>. Be sure to use this only when you are confident
* that all parties accessing the token support the gzip algorithm.</p>
*
* <p>If you're concerned about compatibility, the {@link #DEFLATE DEFLATE} code is JWA standards-compliant.</p>
*/
public static final CompressionCodec GZIP =
Classes.newInstance("io.jsonwebtoken.impl.compression.GzipCompressionCodec");
Classes.newInstance("io.jsonwebtoken.impl.compression.GzipCompressionCodec");
}

View File

@ -30,7 +30,7 @@ import java.util.Map;
* header.{@link Map#put(Object, Object) put}("headerParamName", "headerParamValue");
* </pre>
*
* <h3>Creation</h3>
* <h2>Creation</h2>
*
* <p>It is easiest to create a {@code Header} instance by calling one of the
* {@link Jwts#header() JWTs.header()} factory methods.</p>

View File

@ -21,6 +21,7 @@ import io.jsonwebtoken.io.Encoder;
import io.jsonwebtoken.io.Serializer;
import io.jsonwebtoken.security.InvalidKeyException;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.Date;
import java.util.Map;
@ -365,7 +366,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
/**
* Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS.
*
* <h4>Deprecation Notice: Deprecated as of 0.10.0</h4>
* <p><b>Deprecation Notice: Deprecated as of 0.10.0</b></p>
*
* <p>Use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to
* obtain the {@code Key} and then invoke {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)}.</p>
@ -390,7 +391,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* <p>This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting
* byte array is used to invoke {@link #signWith(SignatureAlgorithm, byte[])}.</p>
*
* <h4>Deprecation Notice: Deprecated as of 0.10.0, will be removed in the 1.0 release.</h4>
* <p><b>Deprecation Notice: Deprecated as of 0.10.0, will be removed in the 1.0 release.</b></p>
*
* <p>This method has been deprecated because the {@code key} argument for this method can be confusing: keys for
* cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were
@ -410,13 +411,12 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* StackOverflow answer</a> explaining why raw (non-base64-encoded) strings are almost always incorrect for
* signature operations.</p>
*
* <p>To perform the correct logic with base64EncodedSecretKey strings with JJWT >= 0.10.0, you may do this:
* <p>To perform the correct logic with base64EncodedSecretKey strings with JJWT &gt;= 0.10.0, you may do this:</p>
* <pre><code>
* byte[] keyBytes = {@link Decoders Decoders}.{@link Decoders#BASE64 BASE64}.{@link Decoder#decode(Object) decode(base64EncodedSecretKey)};
* Key key = {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(keyBytes)};
* jwtBuilder.signWith(key); //or {@link #signWith(Key, SignatureAlgorithm)}
* </code></pre>
* </p>
*
* <p>This method will be removed in the 1.0 release.</p>
*
@ -475,7 +475,7 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
* can be useful. For example, when embedding JWTs in URLs, some browsers may not support URLs longer than a
* certain length. Using compression can help ensure the compact JWT fits within that length. However, NOTE:</p>
*
* <h3>Compatibility Warning</h3>
* <p><b>Compatibility Warning</b></p>
*
* <p>The JWT family of specifications defines compression only for JWE (Json Web Encryption)
* tokens. Even so, JJWT will also support compression for JWS tokens as well if you choose to use it.

View File

@ -37,7 +37,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param id
* @param id the {@code jti} value that must exist in the parsed JWT.
* @return the parser method for chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -54,7 +54,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param subject
* @param subject the {@code sub} value that must exist in the parsed JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -71,7 +71,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param audience
* @param audience the {@code aud} value that must exist in the parsed JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -88,7 +88,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param issuer
* @param issuer the {@code iss} value that must exist in the parsed JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -105,7 +105,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param issuedAt
* @param issuedAt the {@code iat} value that must exist in the parsed JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -122,7 +122,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param expiration
* @param expiration the {@code exp} value that must exist in the parsed JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -139,7 +139,7 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param notBefore
* @param notBefore the {@code nbf} value that must exist in the parsed JWT.
* @return the parser for method chaining
* @see MissingClaimException
* @see IncorrectClaimException
@ -156,8 +156,8 @@ public interface JwtParser {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param claimName
* @param value
* @param claimName the name of a claim that must exist in the parsed JWT.
* @param value the value that must exist for the specified {@code claimName} in the JWT.
* @return the parser for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -190,9 +190,9 @@ public interface JwtParser {
*
* @param seconds the number of seconds to tolerate for clock skew when verifying {@code exp} or {@code nbf} claims.
* @return the parser for method chaining.
* @since 0.7.0
* @throws IllegalArgumentException if {@code seconds} is a value greater than {@code Long.MAX_VALUE / 1000} as
* any such value would cause numeric overflow when multiplying by 1000 to obtain a millisecond value.
* any such value would cause numeric overflow when multiplying by 1000 to obtain a millisecond value.
* @since 0.7.0
* @deprecated see {@link JwtParserBuilder#setAllowedClockSkewSeconds(long)}.
* To construct a JwtParser use the corresponding builder via {@link Jwts#parserBuilder()}. This will construct an
* immutable JwtParser.
@ -204,10 +204,10 @@ public interface JwtParser {
/**
* Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not
* a JWS (no signature), this key is not used.
* <p>
*
* <p>Note that this key <em>MUST</em> be a valid key for the signature algorithm found in the JWT header
* (as the {@code alg} header parameter).</p>
* <p>
*
* <p>This method overwrites any previously set key.</p>
*
* @param key the algorithm-specific signature verification key used to validate any discovered JWS digital
@ -233,7 +233,7 @@ public interface JwtParser {
* <p>This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting
* byte array is used to invoke {@link #setSigningKey(byte[])}.</p>
*
* <h4>Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0</h4>
* <p><b>Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0</b></p>
*
* <p>This method has been deprecated because the {@code key} argument for this method can be confusing: keys for
* cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were
@ -270,10 +270,10 @@ public interface JwtParser {
/**
* Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not
* a JWS (no signature), this key is not used.
* <p>
*
* <p>Note that this key <em>MUST</em> be a valid key for the signature algorithm found in the JWT header
* (as the {@code alg} header parameter).</p>
* <p>
*
* <p>This method overwrites any previously set key.</p>
*
* @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital
@ -290,12 +290,12 @@ public interface JwtParser {
/**
* Sets the {@link SigningKeyResolver} used to acquire the <code>signing key</code> that should be used to verify
* a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
* <p>
*
* <p>Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing
* the JWT and the JWT header or payload (plaintext body or Claims) must be inspected first to determine how to
* look up the signing key. Once returned by the resolver, the JwtParser will then verify the JWS signature with the
* returned key. For example:</p>
* <p>
*
* <pre>
* Jws&lt;Claims&gt; jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
* &#64;Override
@ -305,9 +305,9 @@ public interface JwtParser {
* }})
* .parseClaimsJws(compact);
* </pre>
* <p>
*
* <p>A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.</p>
* <p>
*
* <p>This method should only be used if a signing key is not provided by the other {@code setSigningKey*} builder
* methods.</p>
*
@ -325,11 +325,14 @@ public interface JwtParser {
/**
* Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to
* decompress the JWT body. If the parsed JWT is not compressed, this resolver is not used.
*
* <p><b>NOTE:</b> Compression is not defined by the JWT Specification, and it is not expected that other libraries
* (including JJWT versions &lt; 0.6.0) are able to consume a compressed JWT body correctly. This method is only
* useful if the compact JWT was compressed with JJWT &gt;= 0.6.0 or another library that you know implements
* the same behavior.</p>
* <h3>Default Support</h3>
*
* <p><b>Default Support</b></p>
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
@ -385,12 +388,12 @@ public interface JwtParser {
* <p><b>NOTE: this method will be removed before version 1.0</b>
*/
@Deprecated
JwtParser deserializeJsonWith(Deserializer<Map<String,?>> deserializer);
JwtParser deserializeJsonWith(Deserializer<Map<String, ?>> deserializer);
/**
* Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}
* otherwise.
* <p>
*
* <p>Note that if you are reasonably sure that the token is signed, it is more efficient to attempt to
* parse the token (and catching exceptions if necessary) instead of calling this method first before parsing.</p>
*
@ -403,7 +406,7 @@ public interface JwtParser {
/**
* Parses the specified compact serialized JWT string based on the builder's current configuration state and
* returns the resulting JWT or JWS instance.
* <p>
*
* <p>This method returns a JWT or JWS based on the parsed string. Because it may be cumbersome to determine if it
* is a JWT or JWS, or if the body/payload is a Claims or String with {@code instanceof} checks, the
* {@link #parse(String, JwtHandler) parse(String,JwtHandler)} method allows for a type-safe callback approach that
@ -430,11 +433,11 @@ public interface JwtParser {
/**
* Parses the specified compact serialized JWT string based on the builder's current configuration state and
* invokes the specified {@code handler} with the resulting JWT or JWS instance.
* <p>
*
* <p>If you are confident of the format of the JWT before parsing, you can create an anonymous subclass using the
* {@link io.jsonwebtoken.JwtHandlerAdapter JwtHandlerAdapter} and override only the methods you know are relevant
* for your use case(s), for example:</p>
* <p>
*
* <pre>
* String compactJwt = request.getParameter("jwt"); //we are confident this is a signed JWS
*
@ -445,10 +448,10 @@ public interface JwtParser {
* }
* });
* </pre>
* <p>
*
* <p>If you know the JWT string can be only one type of JWT, then it is even easier to invoke one of the
* following convenience methods instead of this one:</p>
* <p>
*
* <ul>
* <li>{@link #parsePlaintextJwt(String)}</li>
* <li>{@link #parseClaimsJwt(String)}</li>
@ -457,6 +460,8 @@ public interface JwtParser {
* </ul>
*
* @param jwt the compact serialized JWT to parse
* @param handler the handler to invoke when encountering a specific type of JWT
* @param <T> the type of object returned from the {@code handler}
* @return the result returned by the {@code JwtHandler}
* @throws MalformedJwtException if the specified JWT was incorrectly constructed (and therefore invalid).
* Invalid JWTs should not be trusted and should be discarded.
@ -474,17 +479,16 @@ public interface JwtParser {
* @since 0.2
*/
<T> T parse(String jwt, JwtHandler<T> handler)
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
/**
* Parses the specified compact serialized JWT string based on the builder's current configuration state and
* returns
* the resulting unsigned plaintext JWT instance.
* <p>
* returns the resulting unsigned plaintext JWT instance.
*
* <p>This is a convenience method that is usable if you are confident that the compact string argument reflects an
* unsigned plaintext JWT. An unsigned plaintext JWT has a String (non-JSON) body payload and it is not
* cryptographically signed.</p>
* <p>
*
* <p><b>If the compact string presented does not reflect an unsigned plaintext JWT with non-JSON string body,
* an {@link UnsupportedJwtException} will be thrown.</b></p>
*
@ -504,17 +508,16 @@ public interface JwtParser {
* @since 0.2
*/
Jwt<Header, String> parsePlaintextJwt(String plaintextJwt)
throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
/**
* Parses the specified compact serialized JWT string based on the builder's current configuration state and
* returns
* the resulting unsigned plaintext JWT instance.
* <p>
* returns the resulting unsigned plaintext JWT instance.
*
* <p>This is a convenience method that is usable if you are confident that the compact string argument reflects an
* unsigned Claims JWT. An unsigned Claims JWT has a {@link Claims} body and it is not cryptographically
* signed.</p>
* <p>
*
* <p><b>If the compact string presented does not reflect an unsigned Claims JWT, an
* {@link UnsupportedJwtException} will be thrown.</b></p>
*
@ -535,17 +538,16 @@ public interface JwtParser {
* @since 0.2
*/
Jwt<Header, Claims> parseClaimsJwt(String claimsJwt)
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
/**
* Parses the specified compact serialized JWS string based on the builder's current configuration state and
* returns
* the resulting plaintext JWS instance.
* <p>
* returns the resulting plaintext JWS instance.
*
* <p>This is a convenience method that is usable if you are confident that the compact string argument reflects a
* plaintext JWS. A plaintext JWS is a JWT with a String (non-JSON) body (payload) that has been
* cryptographically signed.</p>
* <p>
*
* <p><b>If the compact string presented does not reflect a plaintext JWS, an {@link UnsupportedJwtException}
* will be thrown.</b></p>
*
@ -563,16 +565,15 @@ public interface JwtParser {
* @since 0.2
*/
Jws<String> parsePlaintextJws(String plaintextJws)
throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
throws UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
/**
* Parses the specified compact serialized JWS string based on the builder's current configuration state and
* returns
* the resulting Claims JWS instance.
* <p>
* returns the resulting Claims JWS instance.
*
* <p>This is a convenience method that is usable if you are confident that the compact string argument reflects a
* Claims JWS. A Claims JWS is a JWT with a {@link Claims} body that has been cryptographically signed.</p>
* <p>
*
* <p><b>If the compact string presented does not reflect a Claims JWS, an {@link UnsupportedJwtException} will be
* thrown.</b></p>
*
@ -592,5 +593,5 @@ public interface JwtParser {
* @since 0.2
*/
Jws<Claims> parseClaimsJws(String claimsJws)
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;
}

View File

@ -40,7 +40,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param id
* @param id the required value of the {@code jti} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -52,7 +52,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param subject
* @param subject the required value of the {@code sub} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -64,7 +64,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param audience
* @param audience the required value of the {@code aud} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -76,7 +76,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param issuer
* @param issuer the required value of the {@code iss} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -88,7 +88,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param issuedAt
* @param issuedAt the required value of the {@code iat} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -100,7 +100,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param expiration
* @param expiration the required value of the {@code exp} header parameter.
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -112,7 +112,7 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param notBefore
* @param notBefore the required value of the {@code npf} header parameter.
* @return the parser builder for method chaining
* @see MissingClaimException
* @see IncorrectClaimException
@ -124,8 +124,8 @@ public interface JwtParserBuilder {
* value does not equal the specified value, an exception will be thrown indicating that the
* JWT is invalid and may not be used.
*
* @param claimName
* @param value
* @param claimName the name of a claim that must exist
* @param value the required value of the specified {@code claimName}
* @return the parser builder for method chaining.
* @see MissingClaimException
* @see IncorrectClaimException
@ -155,10 +155,10 @@ public interface JwtParserBuilder {
/**
* Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not
* a JWS (no signature), this key is not used.
* <p>
*
* <p>Note that this key <em>MUST</em> be a valid key for the signature algorithm found in the JWT header
* (as the {@code alg} header parameter).</p>
* <p>
*
* <p>This method overwrites any previously set key.</p>
*
* @param key the algorithm-specific signature verification key used to validate any discovered JWS digital
@ -179,7 +179,7 @@ public interface JwtParserBuilder {
* <p>This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting
* byte array is used to invoke {@link #setSigningKey(byte[])}.</p>
*
* <h4>Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0</h4>
* <p><b>Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0</b></p>
*
* <p>This method has been deprecated because the {@code key} argument for this method can be confusing: keys for
* cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were
@ -211,10 +211,10 @@ public interface JwtParserBuilder {
/**
* Sets the signing key used to verify any discovered JWS digital signature. If the specified JWT string is not
* a JWS (no signature), this key is not used.
* <p>
*
* <p>Note that this key <em>MUST</em> be a valid key for the signature algorithm found in the JWT header
* (as the {@code alg} header parameter).</p>
* <p>
*
* <p>This method overwrites any previously set key.</p>
*
* @param key the algorithm-specific signature verification key to use to validate any discovered JWS digital
@ -226,12 +226,12 @@ public interface JwtParserBuilder {
/**
* Sets the {@link SigningKeyResolver} used to acquire the <code>signing key</code> that should be used to verify
* a JWS's signature. If the parsed String is not a JWS (no signature), this resolver is not used.
* <p>
*
* <p>Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing
* the JWT and the JWT header or payload (plaintext body or Claims) must be inspected first to determine how to
* look up the signing key. Once returned by the resolver, the JwtParser will then verify the JWS signature with the
* returned key. For example:</p>
* <p>
*
* <pre>
* Jws&lt;Claims&gt; jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {
* &#64;Override
@ -241,9 +241,9 @@ public interface JwtParserBuilder {
* }})
* .parseClaimsJws(compact);
* </pre>
* <p>
*
* <p>A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.</p>
* <p>
*
* <p>This method should only be used if a signing key is not provided by the other {@code setSigningKey*} builder
* methods.</p>
*
@ -255,15 +255,19 @@ public interface JwtParserBuilder {
/**
* Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to
* decompress the JWT body. If the parsed JWT is not compressed, this resolver is not used.
*
* <p><b>NOTE:</b> Compression is not defined by the JWT Specification, and it is not expected that other libraries
* (including JJWT versions &lt; 0.6.0) are able to consume a compressed JWT body correctly. This method is only
* useful if the compact JWT was compressed with JJWT &gt;= 0.6.0 or another library that you know implements
* the same behavior.</p>
* <h3>Default Support</h3>
*
* <p><b>Default Support</b></p>
*
* <p>JJWT's default {@link JwtParser} implementation supports both the
* {@link CompressionCodecs#DEFLATE DEFLATE}
* and {@link CompressionCodecs#GZIP GZIP} algorithms by default - you do not need to
* specify a {@code CompressionCodecResolver} in these cases.</p>
*
* <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you must implement
* your own {@link CompressionCodecResolver} and specify that via this method and also when
* {@link io.jsonwebtoken.JwtBuilder#compressWith(CompressionCodec) building} JWTs.</p>

View File

@ -48,6 +48,8 @@ public final class Jwts {
* with the specified name/value pairs. As this is a less common use of JWTs, consider using the
* {@link #jwsHeader(java.util.Map)} factory method instead if you will later digitally sign the JWT.
*
* @param header map of name/value pairs used to create a <em>plaintext</em> (not digitally signed) JWT
* {@code Header} instance.
* @return a new {@link Header} instance suitable for <em>plaintext</em> (not digitally signed) JWTs.
*/
public static Header header(Map<String, Object> header) {
@ -68,6 +70,7 @@ public final class Jwts {
* Returns a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the
* specified name/value pairs.
*
* @param header map of name/value pairs used to create a new {@link JwsHeader} instance.
* @return a new {@link JwsHeader} instance suitable for digitally signed JWTs (aka 'JWS's), populated with the
* specified name/value pairs.
* @see JwtBuilder#setHeader(Header)
@ -122,9 +125,9 @@ public final class Jwts {
}
/**
* Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser).
* Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser}.
*
* @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser).
* @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser}.
*/
public static JwtParserBuilder parserBuilder() {
return Classes.newInstance("io.jsonwebtoken.impl.DefaultJwtParserBuilder");

View File

@ -531,7 +531,7 @@ public enum SignatureAlgorithm {
* Section 3.3)</a> mandates that RSA signing key lengths <em>MUST</em> be 2048 bits or greater.
* {@code RSAKey}s with key lengths less than 2048 bits will be rejected with a
* {@link WeakKeyException}.</li>
* <li>Technically any RSA key of length >= 2048 bits may be used with the {@link #RS256}, {@link #RS384}, and
* <li>Technically any RSA key of length &gt;= 2048 bits may be used with the {@link #RS256}, {@link #RS384}, and
* {@link #RS512} algorithms, so we assume an RSA signature algorithm based on the key length to
* parallel similar decisions in the JWT specification for HMAC and ECDSA signature algorithms.
* This is not required - just a convenience.</li>
@ -545,7 +545,6 @@ public enum SignatureAlgorithm {
* <li>The {@link #RS256}, {@link #RS384}, and {@link #RS512} algorithms are available in the JDK by default
* while the {@code PS}* variants require an additional JCA Provider (like BouncyCastle).</li>
* </ul>
* </p>
*
* <p>Finally, this method will throw an {@link InvalidKeyException} for any key that does not match the
* heuristics and requirements documented above, since that inevitably means the Key is either insufficient or

View File

@ -38,7 +38,7 @@ import java.security.Key;
*
* <p>A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.</p>
*
* <h3>SigningKeyResolverAdapter</h3>
* <h2>Using an Adapter</h2>
*
* <p>If you only need to resolve a signing key for a particular JWS (either a plaintext or Claims JWS), consider using
* the {@link io.jsonwebtoken.SigningKeyResolverAdapter} and overriding only the method you need to support instead of

View File

@ -65,6 +65,7 @@ public final class Classes {
* the JRE's <code>ClassNotFoundException</code>.
*
* @param fqcn the fully qualified class name to load
* @param <T> The type of Class returned
* @return the located class
* @throws UnknownClassException if the class cannot be found.
*/
@ -187,6 +188,14 @@ public final class Classes {
}
/**
* Invokes the fully qualified class name's method named {@code methodName} with parameters of type {@code argTypes}
* using the {@code args} as the method arguments.
* @param fqcn fully qualified class name to locate
* @param methodName name of the method to invoke on the class
* @param argTypes the method argument types supported by the {@code methodName} method
* @param args the runtime arguments to use when invoking the located class method
* @param <T> the expected type of the object returned from the invoked method.
* @return the result returned by the invoked method
* @since 0.10.0
*/
@SuppressWarnings("unchecked")

View File

@ -15,14 +15,8 @@
*/
package io.jsonwebtoken.lang;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
public final class Collections {
@ -225,6 +219,7 @@ public final class Collections {
* Find a single value of the given type in the given Collection.
* @param collection the Collection to search
* @param type the type to look for
* @param <T> the generic type parameter for {@code type}
* @return a value of the given type found if there is a clear match,
* or <code>null</code> if none or more than one such value found
*/
@ -320,6 +315,11 @@ public final class Collections {
* Marshal the elements from the given enumeration into an array of the given type.
* Enumeration elements must be assignable to the type of the given array. The array
* returned will be a different instance than the array given.
* @param enumeration the collection to convert to an array
* @param array an array instance that matches the type of array to return
* @param <A> the element type of the array that will be created
* @param <E> the element type contained within the enumeration.
* @return a new array of type {@code A} that contains the elements in the specified {@code enumeration}.
*/
public static <A,E extends A> A[] toArray(Enumeration<E> enumeration, A[] array) {
ArrayList<A> elements = new ArrayList<A>();
@ -332,6 +332,7 @@ public final class Collections {
/**
* Adapt an enumeration to an iterator.
* @param enumeration the enumeration
* @param <E> the type of elements in the enumeration
* @return the iterator
*/
public static <E> Iterator<E> toIterator(Enumeration<E> enumeration) {

View File

@ -39,7 +39,7 @@ public final class Maps {
* @param value the value of map entry to be added
* @param <K> the maps key type
* @param <V> the maps value type
* Creates a new map builder with a single entry.
* @return a new map builder with a single entry.
*/
public static <K, V> MapBuilder<K, V> of(K key, V value) {
return new HashMapBuilder<K, V>().and(key, value);

View File

@ -73,30 +73,30 @@ public final class Objects {
}
/**
* Determine whether the given object is an array:
* either an Object array or a primitive array.
* Returns {@code true} if the specified argument is an Object or primitive array, {@code false} otherwise.
*
* @param obj the object to check
* @param obj the object instance to check
* @return {@code true} if the specified argument is an Object or primitive array, {@code false} otherwise.
*/
public static boolean isArray(Object obj) {
return (obj != null && obj.getClass().isArray());
}
/**
* Determine whether the given array is empty:
* i.e. <code>null</code> or of zero length.
* {@code true} if the specified array is null or zero length, {@code false} if populated.
*
* @param array the array to check
* @return {@code true} if the specified array is null or zero length, {@code false} if populated.
*/
public static boolean isEmpty(Object[] array) {
return (array == null || array.length == 0);
}
/**
* Returns {@code true} if the specified byte array is null or of zero length, {@code false} otherwise.
* Returns {@code true} if the specified byte array is null or of zero length, {@code false} if populated.
*
* @param array the byte array to check
* @return {@code true} if the specified byte array is null or of zero length, {@code false} otherwise.
* @return {@code true} if the specified byte array is null or of zero length, {@code false} if populated.
*/
public static boolean isEmpty(byte[] array) {
return array == null || array.length == 0;
@ -159,6 +159,7 @@ public final class Objects {
* @param <E> the concrete Enum type
* @param enumValues the array of all Enum constants in question, usually per Enum.values()
* @param constant the constant to get the enum value of
* @return the enum constant of the specified enum type with the specified case-insensitive name
* @throws IllegalArgumentException if the given constant is not found in the given array
* of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to
* avoid this exception.
@ -179,7 +180,9 @@ public final class Objects {
* consisting of the input array contents plus the given object.
*
* @param array the array to append to (can be <code>null</code>)
* @param <A> the type of each element in the specified {@code array}
* @param obj the object to append
* @param <O> the type of the specified object, which must equal to or extend the {@code &lt;A&gt;} type.
* @return the new array (of the same component type; never <code>null</code>)
*/
public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {
@ -306,6 +309,8 @@ public final class Objects {
* @see #nullSafeHashCode(int[])
* @see #nullSafeHashCode(long[])
* @see #nullSafeHashCode(short[])
* @param obj the object to use for obtaining a hashcode
* @return the object's hashcode, which could be 0 if the object is null.
*/
public static int nullSafeHashCode(Object obj) {
if (obj == null) {
@ -346,6 +351,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the array to obtain a hashcode
* @return the array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(Object[] array) {
if (array == null) {
@ -362,6 +369,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the boolean array to obtain a hashcode
* @return the boolean array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(boolean[] array) {
if (array == null) {
@ -378,6 +387,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the byte array to obtain a hashcode
* @return the byte array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(byte[] array) {
if (array == null) {
@ -394,6 +405,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the char array to obtain a hashcode
* @return the char array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(char[] array) {
if (array == null) {
@ -410,6 +423,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the double array to obtain a hashcode
* @return the double array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(double[] array) {
if (array == null) {
@ -426,6 +441,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the float array to obtain a hashcode
* @return the float array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(float[] array) {
if (array == null) {
@ -442,6 +459,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the int array to obtain a hashcode
* @return the int array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(int[] array) {
if (array == null) {
@ -458,6 +477,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the long array to obtain a hashcode
* @return the long array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(long[] array) {
if (array == null) {
@ -474,6 +495,8 @@ public final class Objects {
/**
* Return a hash code based on the contents of the specified array.
* If <code>array</code> is <code>null</code>, this method returns 0.
* @param array the short array to obtain a hashcode
* @return the short array's hashcode, which could be 0 if the array is null.
*/
public static int nullSafeHashCode(short[] array) {
if (array == null) {
@ -490,6 +513,8 @@ public final class Objects {
/**
* Return the same value as <code>{@link Boolean#hashCode()}</code>.
*
* @param bool the boolean to get a hashcode
* @return the same value as {@link Boolean#hashCode()}.
* @see Boolean#hashCode()
*/
public static int hashCode(boolean bool) {
@ -499,6 +524,8 @@ public final class Objects {
/**
* Return the same value as <code>{@link Double#hashCode()}</code>.
*
* @param dbl the double to get a hashcode
* @return the same value as {@link Double#hashCode()}.
* @see Double#hashCode()
*/
public static int hashCode(double dbl) {
@ -509,6 +536,8 @@ public final class Objects {
/**
* Return the same value as <code>{@link Float#hashCode()}</code>.
*
* @param flt the float to get a hashcode
* @return the same value as {@link Float#hashCode()}.
* @see Float#hashCode()
*/
public static int hashCode(float flt) {
@ -518,6 +547,8 @@ public final class Objects {
/**
* Return the same value as <code>{@link Long#hashCode()}</code>.
*
* @param lng the long to get a hashcode
* @return the same value as {@link Long#hashCode()}.
* @see Long#hashCode()
*/
public static int hashCode(long lng) {
@ -532,9 +563,8 @@ public final class Objects {
/**
* Return a String representation of an object's overall identity.
*
* @param obj the object (may be <code>null</code>)
* @return the object's identity as String representation,
* or an empty String if the object was <code>null</code>
* @param obj the object (which may be <code>null</code>).
* @return the object's identity as String representation, or an empty String if the object was <code>null</code>.
*/
public static String identityToString(Object obj) {
if (obj == null) {

View File

@ -16,18 +16,8 @@
package io.jsonwebtoken.lang;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.*;
public final class Strings {
@ -52,7 +42,7 @@ public final class Strings {
/**
* Check that the given CharSequence is neither <code>null</code> nor of length 0.
* Note: Will return <code>true</code> for a CharSequence that purely consists of whitespace.
* <p><pre>
* <pre>
* Strings.hasLength(null) = false
* Strings.hasLength("") = false
* Strings.hasLength(" ") = true
@ -81,7 +71,7 @@ public final class Strings {
* Check whether the given CharSequence has actual text.
* More specifically, returns <code>true</code> if the string not <code>null</code>,
* its length is greater than 0, and it contains at least one non-whitespace character.
* <p><pre>
* <pre>
* Strings.hasText(null) = false
* Strings.hasText("") = false
* Strings.hasText(" ") = false
@ -288,10 +278,10 @@ public final class Strings {
/**
* Test if the given String starts with the specified prefix,
* ignoring upper/lower case.
* Returns {@code true} if the given string starts with the specified case-insensitive prefix, {@code false} otherwise.
* @param str the String to check
* @param prefix the prefix to look for
* @return {@code true} if the given string starts with the specified case-insensitive prefix, {@code false} otherwise.
* @see java.lang.String#startsWith
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
@ -310,10 +300,10 @@ public final class Strings {
}
/**
* Test if the given String ends with the specified suffix,
* ignoring upper/lower case.
* Returns {@code true} if the given string ends with the specified case-insensitive suffix, {@code false} otherwise.
* @param str the String to check
* @param suffix the suffix to look for
* @return {@code true} if the given string ends with the specified case-insensitive suffix, {@code false} otherwise.
* @see java.lang.String#endsWith
*/
public static boolean endsWithIgnoreCase(String str, String suffix) {
@ -333,11 +323,11 @@ public final class Strings {
}
/**
* Test whether the given string matches the given substring
* at the given index.
* Returns {@code true} if the given string matches the given substring at the given index, {@code false} otherwise.
* @param str the original string (or StringBuilder)
* @param index the index in the original string to start matching against
* @param substring the substring to match at the given index
* @return {@code true} if the given string matches the given substring at the given index, {@code false} otherwise.
*/
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
for (int j = 0; j < substring.length(); j++) {
@ -350,9 +340,10 @@ public final class Strings {
}
/**
* Count the occurrences of the substring in string s.
* Returns the number of occurrences the substring {@code sub} appears in string {@code str}.
* @param str string to search in. Return 0 if this is null.
* @param sub string to search for. Return 0 if this is null.
* @return the number of occurrences the substring {@code sub} appears in string {@code str}.
*/
public static int countOccurrencesOf(String str, String sub) {
if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {
@ -457,6 +448,7 @@ public final class Strings {
* Unqualify a string qualified by a '.' dot character. For example,
* "this.name.is.qualified", returns "qualified".
* @param qualifiedName the qualified name
* @return an unqualified string by stripping all previous text before (and including) the last period character.
*/
public static String unqualify(String qualifiedName) {
return unqualify(qualifiedName, '.');
@ -467,6 +459,7 @@ public final class Strings {
* "this:name:is:qualified" returns "qualified" if using a ':' separator.
* @param qualifiedName the qualified name
* @param separator the separator
* @return an unqualified string by stripping all previous text before and including the last {@code separator} character.
*/
public static String unqualify(String qualifiedName, char separator) {
return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);

View File

@ -104,6 +104,7 @@ public final class Keys {
* secure-random generated SecretKey that adheres to the required minimum key length. The lengths are:</p>
*
* <table>
* <caption>JWA HMAC-SHA Key Length Requirements</caption>
* <tr>
* <th>Algorithm</th>
* <th>Key Length</th>
@ -146,6 +147,7 @@ public final class Keys {
* <p>If the {@code alg} argument is an RSA algorithm, a KeyPair is generated based on the following:</p>
*
* <table>
* <caption>Generated RSA Key Sizes</caption>
* <tr>
* <th>JWA Algorithm</th>
* <th>Key Size</th>
@ -179,6 +181,7 @@ public final class Keys {
* <p>If the {@code alg} argument is an Elliptic Curve algorithm, a KeyPair is generated based on the following:</p>
*
* <table>
* <caption>Generated Elliptic Curve Key Parameters</caption>
* <tr>
* <th>JWA Algorithm</th>
* <th>Key Size</th>

View File

@ -17,13 +17,9 @@ package io.jsonwebtoken.impl;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.DateFormats;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class JwtMap implements Map<String, Object> {
@ -62,9 +58,7 @@ public class JwtMap implements Map<String, Object> {
}
}
/**
* @since 0.10.0
*/
// @since 0.10.0
private static Date parseIso8601Date(String s, String name) throws IllegalArgumentException {
try {
return DateFormats.parseIso8601Date(s);
@ -74,9 +68,7 @@ public class JwtMap implements Map<String, Object> {
}
}
/**
* @since 0.10.0
*/
// @since 0.10.0
protected static Date toSpecDate(Object v, String name) {
if (v == null) {
return null;

View File

@ -15,13 +15,10 @@
*/
package io.jsonwebtoken.impl.compression;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.CompressionException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.*;
import io.jsonwebtoken.impl.lang.Services;
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Strings;
import java.util.Collections;
@ -30,7 +27,7 @@ import java.util.Map;
/**
* Default implementation of {@link CompressionCodecResolver} that supports the following:
* <p>
*
* <ul>
* <li>If the specified JWT {@link Header} does not have a {@code zip} header, this implementation does
* nothing and returns {@code null} to the caller, indicating no compression was used.</li>

View File

@ -15,6 +15,9 @@ public final class LegacyServices {
* Wraps {@code Services.loadFirst} and throws a {@link UnknownClassException} instead of a
* {@link UnavailableImplementationException} to retain the previous behavior. This method should be used when
* to retain the previous behavior of methods that throw an unchecked UnknownClassException.
* @param <T> the type of object to return
* @param spi the class for which to find the first instance
* @return the first instance of type {@code T} found from a call to {@link Services#loadFirst(Class)}
*/
public static <T> T loadFirst(Class<T> spi) {
try {

View File

@ -19,9 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
import io.jsonwebtoken.impl.DefaultJwsHeader
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
import io.jsonwebtoken.impl.lang.Services
import io.jsonwebtoken.io.Encoders
import io.jsonwebtoken.io.Serializer
import io.jsonwebtoken.impl.lang.Services
import io.jsonwebtoken.lang.Strings
import io.jsonwebtoken.security.Keys
import io.jsonwebtoken.security.WeakKeyException
@ -38,6 +38,28 @@ import static org.junit.Assert.*
class DeprecatedJwtsTest {
private static Date now() {
return dateWithOnlySecondPrecision(System.currentTimeMillis());
}
private static int later() {
def date = laterDate(10000)
def seconds = date.getTime() / 1000
return seconds as int
}
private static Date laterDate(int seconds) {
def millis = seconds * 1000L
def time = System.currentTimeMillis() + millis
return dateWithOnlySecondPrecision(time)
}
private static Date dateWithOnlySecondPrecision(long millis) {
long seconds = (millis / 1000) as long
long secondOnlyPrecisionMillis = seconds * 1000
return new Date(secondOnlyPrecisionMillis)
}
protected static String base64Url(String s) {
byte[] bytes = s.getBytes(Strings.UTF_8)
return Encoders.BASE64URL.encode(bytes)
@ -243,31 +265,9 @@ class DeprecatedJwtsTest {
assertNull claims.getAudience()
}
private static Date now() {
return dateWithOnlySecondPrecision(System.currentTimeMillis());
}
private static int later() {
return laterDate().getTime() / 1000;
}
private static Date laterDate(int seconds) {
return dateWithOnlySecondPrecision(System.currentTimeMillis() + (seconds * 1000));
}
private static Date laterDate() {
return laterDate(10000);
}
private static Date dateWithOnlySecondPrecision(long millis) {
long seconds = millis / 1000;
long secondOnlyPrecisionMillis = seconds * 1000;
return new Date(secondOnlyPrecisionMillis);
}
@Test
void testConvenienceExpiration() {
Date then = laterDate();
Date then = laterDate(10000)
String compact = Jwts.builder().setExpiration(then).compact();
Claims claims = Jwts.parser().parse(compact).body as Claims
def claimedDate = claims.getExpiration()

View File

@ -19,9 +19,9 @@ import io.jsonwebtoken.impl.DefaultHeader
import io.jsonwebtoken.impl.DefaultJwsHeader
import io.jsonwebtoken.impl.compression.DefaultCompressionCodecResolver
import io.jsonwebtoken.impl.compression.GzipCompressionCodec
import io.jsonwebtoken.impl.lang.Services
import io.jsonwebtoken.io.Encoders
import io.jsonwebtoken.io.Serializer
import io.jsonwebtoken.impl.lang.Services
import io.jsonwebtoken.lang.Strings
import io.jsonwebtoken.security.Keys
import io.jsonwebtoken.security.WeakKeyException
@ -38,6 +38,28 @@ import static org.junit.Assert.*
class JwtsTest {
private static Date now() {
return dateWithOnlySecondPrecision(System.currentTimeMillis());
}
private static int later() {
def date = laterDate(10000)
def seconds = date.getTime() / 1000
return seconds as int
}
private static Date laterDate(int seconds) {
def millis = seconds * 1000L
def time = System.currentTimeMillis() + millis
return dateWithOnlySecondPrecision(time)
}
private static Date dateWithOnlySecondPrecision(long millis) {
long seconds = (millis / 1000) as long
long secondOnlyPrecisionMillis = seconds * 1000
return new Date(secondOnlyPrecisionMillis)
}
protected static String base64Url(String s) {
byte[] bytes = s.getBytes(Strings.UTF_8)
return Encoders.BASE64URL.encode(bytes)
@ -243,31 +265,9 @@ class JwtsTest {
assertNull claims.getAudience()
}
private static Date now() {
return dateWithOnlySecondPrecision(System.currentTimeMillis());
}
private static int later() {
return laterDate().getTime() / 1000;
}
private static Date laterDate(int seconds) {
return dateWithOnlySecondPrecision(System.currentTimeMillis() + (seconds * 1000));
}
private static Date laterDate() {
return laterDate(10000);
}
private static Date dateWithOnlySecondPrecision(long millis) {
long seconds = millis / 1000;
long secondOnlyPrecisionMillis = seconds * 1000;
return new Date(secondOnlyPrecisionMillis);
}
@Test
void testConvenienceExpiration() {
Date then = laterDate();
Date then = laterDate(10000);
String compact = Jwts.builder().setExpiration(then).compact();
Claims claims = Jwts.parserBuilder().build().parse(compact).body as Claims
def claimedDate = claims.getExpiration()

View File

@ -97,13 +97,21 @@ class KeysImplTest {
PublicKey pub = pair.getPublic()
assert pub instanceof ECPublicKey
assertEquals "EC", pub.algorithm
assertEquals jdkParamName, pub.params.name
if (pub.params.hasProperty('name')) { // JDK <= 14
assertEquals jdkParamName, pub.params.name
} else { // JDK >= 15
assertEquals asn1oid, pub.params.nameAndAliases[0]
}
assertEquals alg.minKeyLength, pub.params.order.bitLength()
PrivateKey priv = pair.getPrivate()
assert priv instanceof ECPrivateKey
assertEquals "EC", priv.algorithm
assertEquals jdkParamName, priv.params.name
if (pub.params.hasProperty('name')) { // JDK <= 14
assertEquals jdkParamName, priv.params.name
} else { // JDK >= 15
assertEquals asn1oid, priv.params.nameAndAliases[0]
}
assertEquals alg.minKeyLength, priv.params.order.bitLength()
} else {

245
pom.xml
View File

@ -73,11 +73,10 @@
</repository>
</distributionManagement>
<!-- temporary fix until official release of coverall-maven-plugin with clover support -->
<repositories>
<repository>
<id>ossrh</id>
<name>Sonatype Nexus Snapshots</name>
<name>OSSRH Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
@ -86,6 +85,7 @@
<enabled>true</enabled>
</snapshots>
</repository>
<!-- temporary fix until official release of coverall-maven-plugin with clover support: -->
<repository>
<snapshots>
<enabled>false</enabled>
@ -96,6 +96,7 @@
</repository>
</repositories>
<pluginRepositories>
<!-- temporary fix until official release of coverall-maven-plugin with clover support: -->
<pluginRepository>
<snapshots>
<enabled>false</enabled>
@ -105,9 +106,9 @@
<url>https://dl.bintray.com/jwtk/coveralls-maven-plugin</url>
</pluginRepository>
</pluginRepositories>
<!-- temporary fix until official release of coverall-maven-plugin with clover support -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<arguments/>
@ -117,6 +118,9 @@
<maven.jar.version>3.0.2</maven.jar.version>
<maven.compiler.version>3.8.0</maven.compiler.version>
<maven.javadoc.version>3.1.1</maven.javadoc.version>
<maven.source.version>3.0.1</maven.source.version>
<maven.gpg.version>1.6</maven.gpg.version>
<maven.japicmp.version>0.13.1</maven.japicmp.version>
<gmavenplus.version>1.6.1</gmavenplus.version>
<jdk.version>1.7</jdk.version>
@ -126,17 +130,18 @@
<orgjson.version>20180130</orgjson.version>
<gson.version>2.8.9</gson.version>
<maven.javadoc.additionalOptions></maven.javadoc.additionalOptions>
<!-- Optional Runtime Dependencies: -->
<bouncycastle.version>1.67</bouncycastle.version>
<!-- Test Dependencies: Only required for testing when building. Not required by users at runtime: -->
<groovy.version>2.5.11</groovy.version>
<logback.version>1.2.3</logback.version>
<groovy.version>2.5.16</groovy.version>
<easymock.version>3.6</easymock.version>
<junit.version>4.12</junit.version>
<powermock.version>2.0.0-beta.5</powermock.version> <!-- necessary for Java 9 support -->
<failsafe.plugin.version>2.22.0</failsafe.plugin.version>
<surefire.plugin.version>2.22.0</surefire.plugin.version>
<failsafe.plugin.version>3.0.0-M5</failsafe.plugin.version>
<surefire.plugin.version>3.0.0-M5</surefire.plugin.version>
<clover.version>4.2.1</clover.version>
<clover.db>${jjwt.root}/target/clover/clover.db</clover.db>
<surefire.argLine/>
@ -205,12 +210,6 @@
<dependencies>
<!-- Test Dependencies: Only required for testing when building. Not required by users at runtime: -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
@ -256,10 +255,17 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-gitexe</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
<useReleaseProfile>false</useReleaseProfile>
<arguments>${arguments} -Possrh</arguments>
<releaseProfiles>ossrh</releaseProfiles>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
<plugin>
@ -308,17 +314,63 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.4</version>
<version>${maven.javadoc.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<source>${jdk.version}</source>
<failOnError>true</failOnError>
<failOnWarnings>false</failOnWarnings>
<additionalOptions>${maven.javadoc.additionalOptions}</additionalOptions>
</configuration>
<dependencies>
<!-- Workaround for Java 9 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven.source.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven.gpg.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!-- japicmp will scan code for binary breaking changes, Open api/target/japicmp/japicmp.html
for a report of the changes since ${jjwt.previousVersion} -->
<groupId>com.github.siom79.japicmp</groupId>
<artifactId>japicmp-maven-plugin</artifactId>
<version>0.13.1</version>
<version>${maven.japicmp.version}</version>
<configuration>
<oldVersion>
<dependency>
@ -475,47 +527,10 @@
<plugin>
<groupId>org.openclover</groupId>
<artifactId>clover-maven-plugin</artifactId>
<!--
<configuration>
<excludes>
<!- - leaving out lang as it mostly comes from other sources - ->
<exclude>io/jsonwebtoken/lang/*</exclude>
</excludes>
<methodPercentage>100.000000%</methodPercentage>
<statementPercentage>100.000000%</statementPercentage>
<conditionalPercentage>100.000000%</conditionalPercentage>
<targetPercentage>100.000000%</targetPercentage>
</configuration>
<executions>
<execution>
<id>clover</id>
<phase>test</phase>
<goals>
<goal>instrument</goal>
<goal>aggregate</goal>
<goal>clover</goal>
<goal>check</goal>
</goals>
</execution>
</executions> -->
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.scm</groupId>
<artifactId>maven-scm-provider-gitexe</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<configuration>
<mavenExecutorId>forked-path</mavenExecutorId>
<useReleaseProfile>false</useReleaseProfile>
<arguments>-Possrh -Pdocs -Psign</arguments>
<autoVersionSubmodules>true</autoVersionSubmodules>
</configuration>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
@ -563,52 +578,26 @@
</archive>
</configuration>
</plugin>
<!-- Temporarily host coveralls SNAPSHOT with clover support locally -->
<plugin>
<!-- Temporarily host coveralls SNAPSHOT with clover support locally: -->
<groupId>io.jsonwebtoken.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.4.1</version>
</plugin>
<!-- Temporarily host coveralls SNAPSHOT with clover support locally -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven.javadoc.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<failOnError>false</failOnError>
</configuration>
<dependencies>
<!-- Workaround for Java 9 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>nonJDK7</id>
<id>jdk8AndLater</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>
<properties>
<!-- Turn off JDK >= 8's lint checks: -->
<additionalparam>-Xdoclint:none</additionalparam>
<gmavenplus.version>1.8.1</gmavenplus.version>
<gmavenplus.version>1.13.1</gmavenplus.version>
<groovy.version>3.0.10</groovy.version>
<easymock.version>4.2</easymock.version>
<powermock.version>2.0.2</powermock.version>
<maven.japicmp.version>0.15.6</maven.japicmp.version>
</properties>
</profile>
<profile>
@ -618,29 +607,23 @@
<jdk>[1.9,)</jdk>
</activation>
<properties>
<maven.javadoc.additionalOptions>-html5</maven.javadoc.additionalOptions>
<surefire.argLine>--add-opens java.base/jdk.internal.loader=ALL-UNNAMED</surefire.argLine>
</properties>
</profile>
<profile>
<id>sign</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<id>jdk16AndLater</id>
<activation>
<jdk>[16,)</jdk>
</activation>
<properties>
<surefire.argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.security.util=ALL-UNNAMED
</surefire.argLine>
</properties>
</profile>
<profile>
<id>docs</id>
@ -649,36 +632,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven.javadoc.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<dependencies>
<!-- Workaround for Java 9 -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
@ -690,42 +647,14 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>