Issue
I have a simple method for validating a JWT using the JJWT library:
public void validateExpiryDate(String token) {
try {
byte[] secret = configProperties.getTokenSecret().getBytes(StandardCharsets.UTF_8);
SecretKey hmacKey = new SecretKeySpec(secret, HMAC_SHA_256_ALGORITHM);
Jwts.parserBuilder()
.setSigningKey(hmacKey)
.build()
.parseClaimsJws(token);
} catch (JwtException e) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, INVALID_TOKEN_ERR);
}
}
It takes the secret from the configuration properties and parses the claims. According to the JJWT documentation, this method should throw a JwtException
in case there is anything wrong with the token, and I catch that and re-throw a ResponseStatusException
. So, to test this, I have the following test:
@Test
void validatorShouldThrow_when_tokenIsExpired() {
Date issuedAt = Date.from(Instant.now());
Date expiresAt = Date.from(issuedAt.toInstant().minus(10, ChronoUnit.MINUTES));
String jwt = generator.createJWT(JWT_TEST_NAME, JWT_TEST_MAIL, JWT_TEST_SUBJECT, JWT_TEST_ID, issuedAt, expiresAt, CONFIG_PROPERTIES_SECRET);
assertThrows(ResponseStatusException.class, () -> validator.validateExpiryDate(jwt));
}
The createJWT
method is as follows:
public String createJWT(String name, String email, String subject, String id, Date issuedAt, Date expiresAt, String secret) {
return Jwts.builder()
.claim("name", name)
.claim("email", email)
.setSubject(subject)
.setId(id)
.setIssuedAt(issuedAt)
.setExpiration(expiresAt)
.signWith(SignatureAlgorithm.HS256, Base64.getDecoder().decode(secret))
.compact();
}
So when I run this test, instead of getting the expected ResponseStatusException
, I get a org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <org.springframework.web.server.ResponseStatusException> but was: <java.lang.NoSuchMethodError>
error, and a bit further down the stacktrace - Caused by: java.lang.NoSuchMethodError: io.jsonwebtoken.Jwts.parserBuilder()Lio/jsonwebtoken/JwtParserBuilder;
.
Here are the dependencies I use in the classpath of the class where the validateExpiryDate
method is:
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
I ran the gradle -q dependencies
command in this class path to make sure there are no outdated dependencies sneaking in somewhere, but all of the jjwt
dependencies I see listed are referring to version 0.11.5
so that shouldn't be the issue. Just to make sure it wasn't some cached dependency, I flushed the entire local Maven cache by deleting the repository
folder from C:\Users\<user>\.m2
For the love of me, I can't figure out why it can't find the parserBuilder
method. I'm fairly certain it has something to do with classpaths and dependencies, but I've tried so many combinations already and can't find the correct setup. Any ideas are welcome!
Solution
Turns out I just had to use the exact same dependencies in my main application build file where my tests were, as mentioned in this thread. Before that, I had only the main jjwt
dependency - implementation("io.jsonwebtoken:jjwt:0.9.1")
When I added the below three, the test passes as expected:
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
Answered By - Hristo Naydenov
Answer Checked By - Pedro (JavaFixing Volunteer)