Skip to content

Instantly share code, notes, and snippets.

@soumyadey
Last active June 9, 2022 02:50
Show Gist options
  • Save soumyadey/ff748d3e21daded1a30c544198cd9a5b to your computer and use it in GitHub Desktop.
Save soumyadey/ff748d3e21daded1a30c544198cd9a5b to your computer and use it in GitHub Desktop.
Utility class to parse JWT access token (uncopyrighted). See also: https://stackoverflow.com/questions/61565570/replacement-for-deprecated-spring-security-jwthelper/
package com.sd.utils;
@SuppressWarnings("serial")
public class JwtException extends RuntimeException {
public static JwtException invalidAccessToken(String message) {
return new JwtException(message, null);
}
public static JwtException invalidClaims(String message, Throwable cause) {
return new JwtException(message, cause);
}
private JwtException(String message, Throwable cause) {
super(message, cause);
}
}
package com.sd.utils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Base64;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.sd.utils.JwtException;
import lombok.Getter;
public class JwtUtils {
private static JsonMapper jsonMapper = JsonMapper.builder()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.addModule(new JavaTimeModule())
.build();
private JwtUtils() {
throw new IllegalStateException("Utility class");
}
public static JwtClaims getClaims(String accessToken) {
if (accessToken == null) {
throw JwtException.invalidAccessToken("Null access token");
}
String[] tokens = accessToken.split("\\.");
if (tokens.length != 3) {
throw JwtException.invalidAccessToken("JWT must have 3 tokens");
}
try {
byte[] claimsBytesDecoded = Base64.getDecoder().decode(tokens[1].getBytes(StandardCharsets.UTF_8));
return jsonMapper.readValue(claimsBytesDecoded, JwtClaimsImpl.class);
} catch (IllegalArgumentException | IOException e) {
throw JwtException.invalidClaims("Failed to parse access_token claims", e);
}
}
public static interface JwtClaims {
public String getIssuer();
public Instant getExpirationTime();
public Instant getIssuedAt();
public String getSubject();
public List<String> getAudience();
public List<String> getAuthorities();
public List<String> getScopes();
public String getClientId();
public String getGrantType();
}
/*
* https://tools.ietf.org/html/rfc7519#section-4.1
*/
@Getter
public static class JwtClaimsImpl implements JwtClaims {
@JsonProperty("iss")
private String issuer;
@JsonProperty("exp")
private Instant expirationTime;
@JsonProperty("iat")
private Instant issuedAt;
@JsonProperty("sub")
private String subject;
@JsonProperty("aud")
private List<String> audience;
@JsonProperty("authorities")
private List<String> authorities;
@JsonProperty("scope")
private List<String> scopes;
@JsonProperty("client_id")
private String clientId;
@JsonProperty("grant_type")
private String grantType;
}
}
package com.sd.utils.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import org.junit.jupiter.api.Test;
import com.sd.utils.JwtException;
import com.sd.utils.JwtUtils;
import com.sd.utils.JwtUtils.JwtClaims;
class JwtUtilsTest {
@Test
public void testConstructorIsPrivate() throws NoSuchMethodException {
Constructor<JwtUtils> constructor = JwtUtils.class.getDeclaredConstructor();
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
constructor.setAccessible(true);
InvocationTargetException ex = assertThrows(InvocationTargetException.class, constructor::newInstance);
assertTrue(ex.getCause().getClass().isAssignableFrom(IllegalStateException.class));
}
@Test
void testGetClaims() {
JwtClaims claims = JwtUtils.getClaims("<valid token>");
assertNotNull(claims);
assertEquals("http://localhost:8080/uaa/oauth/token", claims.getIssuer());
assertEquals(1588487944, claims.getExpirationTime().getEpochSecond());
assertEquals(1588444744, claims.getIssuedAt().getEpochSecond());
assertTrue(claims.getAudience().contains("uaa"));
assertTrue(claims.getAuthorities().contains("uaa.resource"));
assertTrue(claims.getScopes().contains("uaa.resource"));
assertEquals("client_credentials", claims.getGrantType());
}
@Test
void testInvalidToken() {
assertThrows(JwtException.class, () -> JwtUtils.getClaims(null));
assertThrows(JwtException.class, () -> JwtUtils.getClaims("a.b"));
assertThrows(JwtException.class, () -> JwtUtils.getClaims("a.b.c"));
assertThrows(JwtException.class, () -> JwtUtils.getClaims("aaa.bbb.ccc"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment