JJWT API - JSON Web Token library API for Java and Android
—
This document covers the core JWT types including Claims, Headers, and token interfaces that represent parsed JWT content in JJWT API.
public interface Jwt<H, P> {
// Core Methods
H getHeader();
P getPayload();
<T> T accept(JwtVisitor<T> visitor);
// Static Visitor Constants for Unsecured JWTs
JwtVisitor<Jwt<Header, byte[]>> UNSECURED_CONTENT = /* visitor implementation */;
JwtVisitor<Jwt<Header, Claims>> UNSECURED_CLAIMS = /* visitor implementation */;
}public interface ProtectedJwt<H extends ProtectedHeader, P> extends Jwt<H, P> {
// Base interface for cryptographically-protected JWTs (JWS and JWE)
}public interface Jws<P> extends ProtectedJwt<JwsHeader, P> {
// JWS-specific method
String getDigest();
// Static Visitor Constants for JWS
JwtVisitor<Jws<byte[]>> CONTENT = /* visitor implementation */;
JwtVisitor<Jws<Claims>> CLAIMS = /* visitor implementation */;
}public interface Jwe<P> extends ProtectedJwt<JweHeader, P> {
// JWE extends ProtectedJwt but doesn't add specific methods
// All functionality inherited from base interfaces
}import io.jsonwebtoken.*;
// Parse different JWT types and access content
JwtParser parser = Jwts.parser().verifyWith(key).build();
// Parse generic JWT (auto-detects type)
Jwt<?, ?> jwt = parser.parse(jwtString);
System.out.println("JWT type: " + jwt.getClass().getSimpleName());
// Parse specific JWS with Claims
Jws<Claims> jws = parser.parseSignedClaims(jwtString);
JwsHeader header = jws.getHeader();
Claims claims = jws.getPayload();
String signature = jws.getDigest();
System.out.println("Algorithm: " + header.getAlgorithm());
System.out.println("Subject: " + claims.getSubject());
System.out.println("Signature: " + signature);
// Use visitor pattern for type-safe processing
String result = jwt.accept(new JwtVisitor<String>() {
@Override
public String visit(Jwt<Header, byte[]> jwt) {
return "Unsecured JWT with " + jwt.getPayload().length + " bytes";
}
@Override
public String visit(Jwt<Header, Claims> jwt) {
return "Unsecured JWT with subject: " + jwt.getPayload().getSubject();
}
@Override
public String visit(Jws<byte[]> jws) {
return "Signed JWT with " + jws.getPayload().length + " bytes";
}
@Override
public String visit(Jws<Claims> jws) {
return "Signed JWT with subject: " + jws.getPayload().getSubject();
}
@Override
public String visit(Jwe<byte[]> jwe) {
return "Encrypted JWT with " + jwe.getPayload().length + " bytes";
}
@Override
public String visit(Jwe<Claims> jwe) {
return "Encrypted JWT with subject: " + jwe.getPayload().getSubject();
}
});public interface Header extends Map<String, Object> {
// Standard Header Parameters
String getType();
String getContentType();
String getAlgorithm();
String getCompressionAlgorithm();
// Standard Header Parameter Names
String TYPE = "typ";
String CONTENT_TYPE = "cty";
String ALGORITHM = "alg";
String COMPRESSION_ALGORITHM = "zip";
}public interface ProtectedHeader extends Header, X509Accessor {
// Additional Protected Header Parameters
String getKeyId();
Set<String> getCritical();
// Additional Parameter Names
String KEY_ID = "kid";
String CRITICAL = "crit";
}public interface JwsHeader extends ProtectedHeader {
// RFC 7797 Unencoded Payload Support
boolean isPayloadEncoded();
// Parameter Name
String PAYLOAD_ENCODED = "b64";
}public interface JweHeader extends ProtectedHeader {
// JWE-specific Header Parameters
String getEncryptionAlgorithm();
String getInitializationVector();
String getAuthenticationTag();
String getEphemeralPublicKey();
String getAgreementPartyUInfo();
String getAgreementPartyVInfo();
Integer getPbes2Count();
String getPbes2Salt();
// JWE Parameter Names
String ENCRYPTION_ALGORITHM = "enc";
String INITIALIZATION_VECTOR = "iv";
String AUTHENTICATION_TAG = "tag";
String EPHEMERAL_PUBLIC_KEY = "epk";
String AGREEMENT_PARTY_U_INFO = "apu";
String AGREEMENT_PARTY_V_INFO = "apv";
String PBES2_COUNT = "p2c";
String PBES2_SALT = "p2s";
}import io.jsonwebtoken.*;
// Access header information from parsed JWT
Jws<Claims> jws = parser.parseSignedClaims(jwtString);
JwsHeader header = jws.getHeader();
// Standard header parameters
String algorithm = header.getAlgorithm(); // "RS256"
String type = header.getType(); // "JWT"
String keyId = header.getKeyId(); // "key-1"
String contentType = header.getContentType(); // null (typically)
// Check for RFC 7797 unencoded payload
boolean isPayloadEncoded = header.isPayloadEncoded(); // true (default)
// Access custom header parameters
Object customParam = header.get("custom");
// JWE header example
Jwe<Claims> jwe = parser.parseEncryptedClaims(jweString);
JweHeader jweHeader = jwe.getHeader();
String encAlgorithm = jweHeader.getEncryptionAlgorithm(); // "A256GCM"
String keyAlgorithm = jweHeader.getAlgorithm(); // "RSA-OAEP"public interface Claims extends Map<String, Object> {
// Standard Claims Accessors
String getIssuer();
String getSubject();
Set<String> getAudience();
Date getExpiration();
Date getNotBefore();
Date getIssuedAt();
String getId();
// Type-safe Claim Access
<T> T get(String claimName, Class<T> requiredType);
// Standard Claim Names
String ISSUER = "iss";
String SUBJECT = "sub";
String AUDIENCE = "aud";
String EXPIRATION = "exp";
String NOT_BEFORE = "nbf";
String ISSUED_AT = "iat";
String ID = "jti";
}public interface ClaimsBuilder extends MapMutator<String, Object, ClaimsBuilder>, ClaimsMutator<ClaimsBuilder> {
// Inherits methods from MapMutator and ClaimsMutator
Claims build();
}public interface ClaimsMutator<T> {
// Standard Claims Mutators
T issuer(String iss);
T subject(String sub);
T audience(String aud);
T audiences(String... audiences);
T expiration(Date exp);
T notBefore(Date nbf);
T issuedAt(Date iat);
T id(String jti);
}import io.jsonwebtoken.*;
import java.util.Date;
import java.util.Set;
// Parse JWT and access claims
Jws<Claims> jws = parser.parseSignedClaims(jwtString);
Claims claims = jws.getPayload();
// Standard claims
String issuer = claims.getIssuer(); // "https://auth.example.com"
String subject = claims.getSubject(); // "user123"
Set<String> audience = claims.getAudience(); // ["web-app", "mobile-app"]
Date expiration = claims.getExpiration(); // expiration timestamp
Date notBefore = claims.getNotBefore(); // not-before timestamp
Date issuedAt = claims.getIssuedAt(); // issued-at timestamp
String jti = claims.getId(); // "unique-token-id"
// Type-safe custom claim access
String role = claims.get("role", String.class);
Integer userId = claims.get("user_id", Integer.class);
List<String> permissions = claims.get("permissions", List.class);
// Check for claim existence
if (claims.containsKey("department")) {
String department = claims.get("department", String.class);
}
// Iterate over all claims
for (Map.Entry<String, Object> entry : claims.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Create claims using builder
Claims customClaims = Jwts.claims()
.issuer("myapp")
.subject("user456")
.audience("api")
.expiration(new Date(System.currentTimeMillis() + 3600000))
.add("role", "admin")
.add("permissions", Arrays.asList("read", "write", "delete"))
.build();public interface JwtVisitor<T> {
T visit(Jwt<Header, byte[]> jwt);
T visit(Jwt<Header, Claims> jwt);
T visit(Jws<byte[]> jws);
T visit(Jws<Claims> jws);
T visit(Jwe<byte[]> jwe);
T visit(Jwe<Claims> jwe);
}public class SupportedJwtVisitor<T> implements JwtVisitor<T> {
// Base implementation that throws UnsupportedJwtException for unsupported types
// Subclasses override only methods for supported JWT types
}public interface JwtHandler<T> {
T onUnsecuredContent(Jwt<Header, byte[]> jwt);
T onUnsecuredClaims(Jwt<Header, Claims> jwt);
T onSignedContent(Jws<byte[]> jws);
T onSignedClaims(Jws<Claims> jws);
T onEncryptedContent(Jwe<byte[]> jwe);
T onEncryptedClaims(Jwe<Claims> jwe);
}public class JwtHandlerAdapter<T> implements JwtHandler<T> {
// Default implementation that throws UnsupportedJwtException
// Subclasses override only methods for supported JWT types
}import io.jsonwebtoken.*;
// Custom visitor to extract information
public class JwtInfoVisitor implements JwtVisitor<String> {
@Override
public String visit(Jwt<Header, byte[]> jwt) {
return "Unsecured JWT with byte payload, size: " + jwt.getPayload().length;
}
@Override
public String visit(Jwt<Header, Claims> jwt) {
Claims claims = jwt.getPayload();
return "Unsecured JWT, subject: " + claims.getSubject();
}
@Override
public String visit(Jws<byte[]> jws) {
return "Signed JWT (" + jws.getHeader().getAlgorithm() + ") with byte payload";
}
@Override
public String visit(Jws<Claims> jws) {
Claims claims = jws.getPayload();
return "Signed JWT, subject: " + claims.getSubject() +
", algorithm: " + jws.getHeader().getAlgorithm();
}
@Override
public String visit(Jwe<byte[]> jwe) {
return "Encrypted JWT with byte payload";
}
@Override
public String visit(Jwe<Claims> jwe) {
Claims claims = jwe.getPayload();
return "Encrypted JWT, subject: " + claims.getSubject();
}
}
// Use the visitor
Jwt<?, ?> jwt = parser.parse(jwtString);
String info = jwt.accept(new JwtInfoVisitor());
System.out.println(info);
// Using SupportedJwtVisitor for specific JWT types only
public class ClaimsExtractor extends SupportedJwtVisitor<Claims> {
@Override
public Claims visit(Jwt<Header, Claims> jwt) {
return jwt.getPayload();
}
@Override
public Claims visit(Jws<Claims> jws) {
return jws.getPayload();
}
@Override
public Claims visit(Jwe<Claims> jwe) {
return jwe.getPayload();
}
// Byte payload visits will throw UnsupportedJwtException
}
// Extract claims regardless of JWT type
Claims claims = jwt.accept(new ClaimsExtractor());public interface Locator<T> {
T locate(Header header);
}public abstract class LocatorAdapter<T> implements Locator<T> {
@Override
public T locate(Header header) {
return null; // Default implementation
}
}import io.jsonwebtoken.security.SigningKeyResolver;
// Dynamic key resolution based on header information
public class CustomKeyLocator implements Locator<Key> {
private final KeyService keyService;
public CustomKeyLocator(KeyService keyService) {
this.keyService = keyService;
}
@Override
public Key locate(Header header) {
String keyId = header instanceof ProtectedHeader
? ((ProtectedHeader) header).getKeyId()
: null;
String algorithm = header.getAlgorithm();
if (keyId != null) {
return keyService.getKeyById(keyId);
} else if ("HS256".equals(algorithm)) {
return keyService.getDefaultHmacKey();
} else {
throw new IllegalArgumentException("Cannot locate key for algorithm: " + algorithm);
}
}
}
// Use with parser
JwtParser parser = Jwts.parser()
.keyLocator(new CustomKeyLocator(keyService))
.build();public class ClaimJwtException extends JwtException {
// Base exception for claim validation failures
public ClaimJwtException(Header header, Claims claims, String message);
public ClaimJwtException(Header header, Claims claims, String message, Throwable cause);
public Header getHeader();
public Claims getClaims();
}
public class IncorrectClaimException extends ClaimJwtException {
// Claim has incorrect value
public IncorrectClaimException(Header header, Claims claims, String claimName, Object expectedValue, Object actualValue);
public String getClaimName();
public Object getExpectedValue();
public Object getActualValue();
}
public class MissingClaimException extends ClaimJwtException {
// Required claim is missing
public MissingClaimException(Header header, Claims claims, String claimName);
public String getClaimName();
}
public class InvalidClaimException extends ClaimJwtException {
// Claim value is invalid (e.g., malformed date)
public InvalidClaimException(Header header, Claims claims, String message);
}public class ExpiredJwtException extends ClaimJwtException {
// JWT has expired
public ExpiredJwtException(Header header, Claims claims, String message);
}
public class PrematureJwtException extends ClaimJwtException {
// JWT is not yet valid (nbf claim)
public PrematureJwtException(Header header, Claims claims, String message);
}
public class RequiredTypeException extends JwtException {
// Type conversion failed
public RequiredTypeException(String message);
}import io.jsonwebtoken.*;
try {
Jws<Claims> jws = parser.parseSignedClaims(jwtString);
Claims claims = jws.getPayload();
// JWT is valid, process claims
processValidJwt(claims);
} catch (ExpiredJwtException e) {
System.err.println("JWT expired at: " + e.getClaims().getExpiration());
// Handle expired token
} catch (PrematureJwtException e) {
System.err.println("JWT not valid until: " + e.getClaims().getNotBefore());
// Handle premature token
} catch (MissingClaimException e) {
System.err.println("Missing required claim: " + e.getClaimName());
// Handle missing claim
} catch (IncorrectClaimException e) {
System.err.println("Incorrect claim '" + e.getClaimName() +
"': expected '" + e.getExpectedValue() +
"', got '" + e.getActualValue() + "'");
// Handle incorrect claim
} catch (SignatureException e) {
System.err.println("JWT signature validation failed");
// Handle signature failure
} catch (MalformedJwtException e) {
System.err.println("JWT is malformed: " + e.getMessage());
// Handle malformed JWT
} catch (JwtException e) {
System.err.println("JWT processing failed: " + e.getMessage());
// Handle other JWT exceptions
}import io.jsonwebtoken.Claims;
public class ClaimsUtils {
public static <T> T getClaimSafely(Claims claims, String claimName, Class<T> type, T defaultValue) {
try {
T value = claims.get(claimName, type);
return value != null ? value : defaultValue;
} catch (RequiredTypeException e) {
return defaultValue;
}
}
public static List<String> getStringList(Claims claims, String claimName) {
Object value = claims.get(claimName);
if (value instanceof List) {
List<?> list = (List<?>) value;
return list.stream()
.filter(String.class::isInstance)
.map(String.class::cast)
.collect(Collectors.toList());
}
return Collections.emptyList();
}
public static Date getDateClaim(Claims claims, String claimName) {
Object value = claims.get(claimName);
if (value instanceof Date) {
return (Date) value;
} else if (value instanceof Number) {
return new Date(((Number) value).longValue() * 1000);
}
return null;
}
}
// Usage
Claims claims = jws.getPayload();
String role = ClaimsUtils.getClaimSafely(claims, "role", String.class, "user");
List<String> permissions = ClaimsUtils.getStringList(claims, "permissions");
Date customDate = ClaimsUtils.getDateClaim(claims, "custom_date");Install with Tessl CLI
npx tessl i tessl/maven-io-jsonwebtoken--jjwt-api