mirror of https://github.com/jwtk/jjwt.git
Compare commits
9 Commits
e389800bda
...
ad8f010902
Author | SHA1 | Date |
---|---|---|
Pieter Van Eeckhout | ad8f010902 | |
Andy Boothe | 2ad964a3f1 | |
pveeckhout | 4a76b69d59 | |
pveeckhout | a6a79508b0 | |
pveeckhout | 5a4992b91a | |
pveeckhout | de900cd1ab | |
Pieter Van Eeckhout | 034ae864b6 | |
pveeckhout | e91b0425bf | |
pveeckhout | ff6e2dfbc6 |
|
@ -42,6 +42,7 @@ import java.security.interfaces.ECKey;
|
|||
import java.security.interfaces.RSAKey;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A builder for constructing Unprotected JWTs, Signed JWTs (aka 'JWS's) and Encrypted JWTs (aka 'JWE's).
|
||||
|
@ -585,6 +586,26 @@ public interface JwtBuilder extends ClaimsMutator<JwtBuilder> {
|
|||
// for better/targeted JavaDoc
|
||||
JwtBuilder id(String jti);
|
||||
|
||||
/**
|
||||
* Sets the JWT Claims <a href="https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4">
|
||||
* <code>exp</code></a> (expiration) claim. It will set the expiration Date to the issuedAt time plus the duration
|
||||
* specified if it has been set, otherwise it will use the current system time plus the duration specified
|
||||
*
|
||||
* <p>A JWT obtained after this timestamp should not be used.</p>
|
||||
*
|
||||
* <p>This is a convenience wrapper for:</p>
|
||||
* <blockquote><pre>
|
||||
* {@link #claims()}.{@link ClaimsMutator#expiration(Date) expiration(exp)}.{@link BuilderClaims#and() and()}</pre></blockquote>
|
||||
*
|
||||
* @param duration The duration after the issue time that the JWT should expire. It is added to the issue time to
|
||||
* calculate the expiration time.
|
||||
* @param timeUnit The time unit of the duration parameter. This specifies the unit of measurement for the
|
||||
* duration (e.g., seconds, minutes, hours, etc.), determining how the duration value should
|
||||
* be interpreted when calculating the expiration time.
|
||||
* @return the builder instance for method chaining.
|
||||
*/
|
||||
JwtBuilder expireAfter(long duration, TimeUnit timeUnit);
|
||||
|
||||
/**
|
||||
* Signs the constructed JWT with the specified key using the key's <em>recommended signature algorithm</em>
|
||||
* as defined below, producing a JWS. If the recommended signature algorithm isn't sufficient for your needs,
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright 2018 JWTK
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-root</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>jjwt-bom</artifactId>
|
||||
<name>JJWT :: BOM</name>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<jjwt.root>${basedir}/..</jjwt.root>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Core -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Extensions -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-orgjson</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-gson</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.12.7-SNAPSHOT</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
|
@ -31,6 +31,7 @@ import io.jsonwebtoken.impl.io.UncloseableInputStream;
|
|||
import io.jsonwebtoken.impl.lang.Bytes;
|
||||
import io.jsonwebtoken.impl.lang.Function;
|
||||
import io.jsonwebtoken.impl.lang.Functions;
|
||||
import io.jsonwebtoken.impl.lang.JwtDateConverter;
|
||||
import io.jsonwebtoken.impl.lang.Parameter;
|
||||
import io.jsonwebtoken.impl.lang.Services;
|
||||
import io.jsonwebtoken.impl.security.DefaultAeadRequest;
|
||||
|
@ -76,7 +77,9 @@ import java.security.SecureRandom;
|
|||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DefaultJwtBuilder implements JwtBuilder {
|
||||
|
||||
|
@ -477,6 +480,27 @@ public class DefaultJwtBuilder implements JwtBuilder {
|
|||
return claims().id(jti).and();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JwtBuilder expireAfter(final long duration, final TimeUnit timeUnit) { // TODO: use java.time and optionals from jdk 8 for version 1.0
|
||||
Assert.gt(duration, 0L, "duration must be > 0.");
|
||||
Assert.notNull(timeUnit, "timeUnit cannot be null.");
|
||||
|
||||
Date issuedAtDate = this.claimsBuilder.get(DefaultClaims.ISSUED_AT);
|
||||
long expiryEpochMillis;
|
||||
if (null != issuedAtDate) {
|
||||
expiryEpochMillis = issuedAtDate.getTime() + timeUnit.toMillis(duration);
|
||||
} else {
|
||||
expiryEpochMillis = (System.currentTimeMillis() + timeUnit.toMillis(duration));
|
||||
}
|
||||
Date expiryDate = JwtDateConverter.INSTANCE.applyFrom(expiryEpochMillis / 1000L);
|
||||
|
||||
/*Instant expiryInstant = Optional.ofNullable(this.claimsBuilder.get(DefaultClaims.ISSUED_AT)) // this should return an instant I guess
|
||||
.orElseGet(() -> Instant.now())
|
||||
.plus(duration, timeUnit);*/
|
||||
|
||||
return claims().expiration(expiryDate).and();
|
||||
}
|
||||
|
||||
private void assertPayloadEncoding(String type) {
|
||||
if (!this.encodePayload) {
|
||||
String msg = "Payload encoding may not be disabled for " + type + "s, only JWSs.";
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.junit.Test
|
|||
|
||||
import javax.crypto.Mac
|
||||
import javax.crypto.SecretKey
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import static org.junit.Assert.*
|
||||
|
||||
|
@ -277,6 +278,75 @@ class DefaultJwtParserTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiredAfterDurationValidationMessage() {
|
||||
def duration = -1L
|
||||
def timeUnit = TimeUnit.MINUTES
|
||||
try {
|
||||
Jwts.builder().expireAfter(duration, timeUnit).compact()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
String msg = "duration must be > 0."
|
||||
assertEquals msg, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiredAfterTimeUnitValidationMessage() {
|
||||
def duration = 15L
|
||||
def timeUnit = null
|
||||
try {
|
||||
Jwts.builder().expireAfter(duration, timeUnit).compact()
|
||||
} catch (IllegalArgumentException expected) {
|
||||
String msg = "timeUnit cannot be null."
|
||||
assertEquals msg, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testExpiredAfterExceptionMessage() {
|
||||
long differenceMillis = 781 // arbitrary, anything > 0 is fine
|
||||
def duration = 15L
|
||||
def timeUnit = TimeUnit.MINUTES
|
||||
def expectedExpiry = JwtDateConverter.INSTANCE.applyFrom((System.currentTimeMillis() + timeUnit.toMillis(duration)) / 1000L)
|
||||
def later = new Date(expectedExpiry.getTime() + differenceMillis)
|
||||
def s = Jwts.builder().expireAfter(duration, timeUnit).compact()
|
||||
|
||||
try {
|
||||
Jwts.parser().unsecured().clock(new FixedClock(later)).build().parse(s)
|
||||
} catch (ExpiredJwtException expected) {
|
||||
def exp8601 = DateFormats.formatIso8601(expectedExpiry, true)
|
||||
def later8601 = DateFormats.formatIso8601(later, true)
|
||||
String msg = "JWT expired ${differenceMillis} milliseconds ago at ${exp8601}. " +
|
||||
"Current time: ${later8601}. Allowed clock skew: 0 milliseconds."
|
||||
assertEquals msg, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiredAfterWithIssuedAtExceptionMessage() {
|
||||
long differenceMillis = 781 // arbitrary, anything > 0 is fine
|
||||
def duration = 15L
|
||||
def timeUnit = TimeUnit.MINUTES
|
||||
def issuedAt = JwtDateConverter.INSTANCE.applyFrom((System.currentTimeMillis() + timeUnit.toMillis(-1L)) / 1000L) //set it to one minute earlier
|
||||
def expectedExpiry = JwtDateConverter.INSTANCE.applyFrom((System.currentTimeMillis() + timeUnit.toMillis(duration - 1L)) / 1000L) // we expect it to expire a minute earlier
|
||||
def later = new Date(expectedExpiry.getTime() + differenceMillis)
|
||||
def s = Jwts.builder()
|
||||
.issuedAt(issuedAt)
|
||||
.expireAfter(duration, timeUnit)
|
||||
.compact()
|
||||
|
||||
try {
|
||||
Jwts.parser().unsecured().clock(new FixedClock(later)).build().parse(s)
|
||||
} catch (ExpiredJwtException expected) {
|
||||
def exp8601 = DateFormats.formatIso8601(expectedExpiry, true)
|
||||
def later8601 = DateFormats.formatIso8601(later, true)
|
||||
String msg = "JWT expired ${differenceMillis} milliseconds ago at ${exp8601}. " +
|
||||
"Current time: ${later8601}. Allowed clock skew: 0 milliseconds."
|
||||
assertEquals msg, expected.message
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNotBeforeExceptionMessage() {
|
||||
|
||||
|
@ -291,7 +361,7 @@ class DefaultJwtParserTest {
|
|||
def nbf8601 = DateFormats.formatIso8601(nbf, true)
|
||||
def earlier8601 = DateFormats.formatIso8601(earlier, true)
|
||||
String msg = "JWT early by ${differenceMillis} milliseconds before ${nbf8601}. " +
|
||||
"Current time: ${earlier8601}. Allowed clock skew: 0 milliseconds.";
|
||||
"Current time: ${earlier8601}. Allowed clock skew: 0 milliseconds."
|
||||
assertEquals msg, expected.message
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue