109: enabled injection of a time source - a 'Clock'

This commit is contained in:
Les Hazlewood 2016-04-01 18:15:37 -07:00
parent 13d2e8370a
commit 72e0e3b23c
8 changed files with 87 additions and 28 deletions

View File

@ -1,7 +1,22 @@
package io.jsonwebtoken; package io.jsonwebtoken;
import io.jsonwebtoken.impl.DefaultClock;
import java.util.Date; import java.util.Date;
/**
* A clock represents a time source that can be used when creating and verifying JWTs.
*
* @since 0.7.0
*/
public interface Clock { public interface Clock {
public static final Clock DEFAULT = new DefaultClock();
/**
* Returns the clock's current timestamp at the instant the method is invoked.
*
* @return the clock's current timestamp at the instant the method is invoked.
*/
Date now(); Date now();
} }

View File

@ -1,10 +0,0 @@
package io.jsonwebtoken;
import java.util.Date;
public class DefaultClock implements Clock {
@Override
public Date now() {
return new Date();
}
}

View File

@ -15,6 +15,8 @@
*/ */
package io.jsonwebtoken; package io.jsonwebtoken;
import io.jsonwebtoken.impl.DefaultClock;
import java.security.Key; import java.security.Key;
import java.util.Date; import java.util.Date;
@ -125,11 +127,12 @@ public interface JwtParser {
JwtParser require(String claimName, Object value); JwtParser require(String claimName, Object value);
/** /**
* Replace the {@code clock} used by the parser to determine the current time-of-day to use when validating * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT.
* the parsed JWT. If {@code null}, will reset the behavior to use the system clock. * The parser uses a {@link DefaultClock DefaultClock} instance by default.
* *
* @param clock a {@code Clock} object to return the time-of-day or {@code null} * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT.
* @return the builder instance for method chaining. * @return the builder instance for method chaining.
* @since 0.7.0
*/ */
JwtParser setClock(Clock clock); JwtParser setClock(Clock clock);

View File

@ -0,0 +1,23 @@
package io.jsonwebtoken.impl;
import io.jsonwebtoken.Clock;
import java.util.Date;
/**
* Default {@link Clock} implementation.
*
* @since 0.7.0
*/
public class DefaultClock implements Clock {
/**
* Simply returns <code>new {@link Date}()</code>.
*
* @return a new {@link Date} instance.
*/
@Override
public Date now() {
return new Date();
}
}

View File

@ -21,7 +21,6 @@ import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock; import io.jsonwebtoken.Clock;
import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver; import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.DefaultClock;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header; import io.jsonwebtoken.Header;
import io.jsonwebtoken.IncorrectClaimException; import io.jsonwebtoken.IncorrectClaimException;
@ -71,7 +70,7 @@ public class DefaultJwtParser implements JwtParser {
Claims expectedClaims = new DefaultClaims(); Claims expectedClaims = new DefaultClaims();
private Clock clock = new DefaultClock(); private Clock clock = Clock.DEFAULT;
@Override @Override
public JwtParser requireIssuedAt(Date issuedAt) { public JwtParser requireIssuedAt(Date issuedAt) {
@ -133,12 +132,8 @@ public class DefaultJwtParser implements JwtParser {
@Override @Override
public JwtParser setClock(Clock clock) { public JwtParser setClock(Clock clock) {
if (clock == null) { Assert.notNull(clock, "Clock instance cannot be null.");
this.clock = new DefaultClock(); this.clock = clock;
} else {
this.clock = clock;
}
return this; return this;
} }

View File

@ -4,13 +4,30 @@ import io.jsonwebtoken.Clock;
import java.util.Date; import java.util.Date;
/**
* A {@code Clock} implementation that is constructed with a seed timestamp and always reports that same
* timestamp.
*
* @since 0.7.0
*/
public class FixedClock implements Clock { public class FixedClock implements Clock {
private final Date now; private final Date now;
/**
* Creates a new fixed clock using <code>new {@link Date Date}()</code> as the seed timestamp. All calls to
* {@link #now now()} will always return this seed Date.
*/
public FixedClock() { public FixedClock() {
this(new Date()); this(new Date());
} }
/**
* Creates a new fixed clock using the specified seed timestamp. All calls to
* {@link #now now()} will always return this seed Date.
*
* @param now the specified Date to always return from all calls to {@link #now now()}.
*/
public FixedClock(Date now) { public FixedClock(Date now) {
this.now = now; this.now = now;
} }

View File

@ -15,6 +15,7 @@
*/ */
package io.jsonwebtoken package io.jsonwebtoken
import io.jsonwebtoken.impl.DefaultClock
import io.jsonwebtoken.impl.FixedClock import io.jsonwebtoken.impl.FixedClock
import io.jsonwebtoken.impl.TextCodec import io.jsonwebtoken.impl.TextCodec
import org.junit.Test import org.junit.Test
@ -1407,15 +1408,11 @@ class JwtParserTest {
@Test @Test
void testParseClockManipulationWithNullClock() { void testParseClockManipulationWithNullClock() {
Date expiry = new Date(System.currentTimeMillis() - 1000) JwtParser parser = Jwts.parser();
String compact = Jwts.builder().setSubject('Joe').setExpiration(expiry).compact()
try { try {
Jwts.parser().setClock(null).parse(compact) parser.setClock(null)
fail() fail()
} catch (ExpiredJwtException e) { } catch (IllegalArgumentException expected) {
assertTrue e.getMessage().startsWith('JWT expired at ')
} }
} }

View File

@ -0,0 +1,19 @@
package io.jsonwebtoken.impl
import org.junit.Test
import static org.junit.Assert.*
class FixedClockTest {
@Test
void testFixedClockDefaultConstructor() {
def clock = new FixedClock()
def date1 = clock.now()
Thread.sleep(100)
def date2 = clock.now()
assertSame date1, date2
}
}