@Override
publicJws<Claims> parseClaimsJws(String claimsJws) {
return parse(claimsJws,newJwtHandlerAdapter<Jws<Claims>>() {
@Override
public Jws<Claims> onClaimsJws(Jws<Claims> jws) {
return jws;
}
});
}
Plain Text
볡μ¬
@Override
public<T> T parse(String compact,JwtHandler<T> handler)
throwsExpiredJwtException, MalformedJwtException, SignatureException {
Assert.notNull(handler, "JwtHandler argument cannot be null.");
Assert.hasText(compact, "JWT String argument cannot be null or empty.");
Jwtjwt = parse(compact);
if(jwtinstanceofJws) {
Jwsjws = (Jws) jwt;
Object body = jws.getBody();
if(bodyinstanceofClaims) {
returnhandler.onClaimsJws((Jws<Claims>) jws);
}else{
returnhandler.onPlaintextJws((Jws<String>) jws);
}
}else{
Object body = jwt.getBody();
if(bodyinstanceofClaims) {
returnhandler.onClaimsJwt((Jwt<Header,Claims>) jwt);
}else{
returnhandler.onPlaintextJwt((Jwt<Header, String>) jwt);
}
}
}
Plain Text
볡μ¬
@Override
publicJwtparse(String jwt)throwsExpiredJwtException, MalformedJwtException, SignatureException {
// TODO, this logic is only need for a now deprecated code path
// remove this block in v1.0 (the equivalent is already in DefaultJwtParserBuilder)
if(this.deserializer ==null) {
// try to find one based on the services available
// TODO: This util class will throw a UnavailableImplementationException here to retain behavior of previous version, remove in v1.0
this.deserializer = LegacyServices.loadFirst(Deserializer.class);
}
Assert.hasText(jwt, "JWT String argument cannot be null or empty.");
if("..".equals(jwt)) {
String msg = "JWT string '..' is missing a header.";
throw newMalformedJwtException(msg);
}
String base64UrlEncodedHeader =null;
String base64UrlEncodedPayload =null;
String base64UrlEncodedDigest =null;
intdelimiterCount = 0;
StringBuilder sb =newStringBuilder(128);
for(charc : jwt.toCharArray()) {
if(c ==SEPARATOR_CHAR) {
CharSequencetokenSeq = Strings.clean(sb);
String token = tokenSeq !=null? tokenSeq.toString() :null;
if(delimiterCount == 0) {
base64UrlEncodedHeader = token;
}else if(delimiterCount == 1) {
base64UrlEncodedPayload = token;
}
delimiterCount++;
sb.setLength(0);
}else{
sb.append(c);
}
}
if(delimiterCount != 2) {
String msg = "JWT strings must contain exactly 2 period characters. Found: " + delimiterCount;
throw newMalformedJwtException(msg);
}
if(sb.length() > 0) {
base64UrlEncodedDigest = sb.toString();
}
// =============== Header =================
Headerheader =null;
CompressionCodeccompressionCodec =null;
if(base64UrlEncodedHeader !=null) {
byte[] bytes = base64UrlDecoder.decode(base64UrlEncodedHeader);
String origValue =newString(bytes, Strings.UTF_8);
Map<String, Object> m = (Map<String, Object>) readValue(origValue);
if(base64UrlEncodedDigest !=null) {
header =newDefaultJwsHeader(m);
}else{
header =newDefaultHeader(m);
}
compressionCodec = compressionCodecResolver.resolveCompressionCodec(header);
}
// =============== Body =================
String payload = "";//https://github.com/jwtk/jjwt/pull/540
if(base64UrlEncodedPayload !=null) {
byte[] bytes = base64UrlDecoder.decode(base64UrlEncodedPayload);
if(compressionCodec !=null) {
bytes = compressionCodec.decompress(bytes);
}
payload =newString(bytes, Strings.UTF_8);
}
Claimsclaims =null;
if(!payload.isEmpty() && payload.charAt(0) == '{' && payload.charAt(payload.length() - 1) == '}') {//likely to be json, parse it:
Map<String, Object> claimsMap = (Map<String, Object>) readValue(payload);
claims =newDefaultClaims(claimsMap);
}
// =============== Signature =================
if(base64UrlEncodedDigest !=null) {//it is signed - validate the signature
JwsHeaderjwsHeader = (JwsHeader) header;
SignatureAlgorithm algorithm =null;
if(header !=null) {
String alg = jwsHeader.getAlgorithm();
if(Strings.hasText(alg)) {
algorithm = SignatureAlgorithm.forName(alg);
}
}
if(algorithm ==null|| algorithm == SignatureAlgorithm.NONE) {
//it is plaintext, but it has a signature. This is invalid:
String msg = "JWT string has a digest/signature, but the header does not reference a valid signature " +
"algorithm.";
throw newMalformedJwtException(msg);
}
if(key !=null&& keyBytes !=null) {
throw newIllegalStateException("A key object and key bytes cannot both be specified. Choose either.");
}else if((key !=null|| keyBytes !=null) && signingKeyResolver !=null) {
String object = key !=null? "a key object" : "key bytes";
throw newIllegalStateException("A signing key resolver and " + object + " cannot both be specified. Choose either.");
}
//digitally signed, let's assert the signature:
Keykey =this.key;
if(key ==null) {//fall back to keyBytes
byte[] keyBytes =this.keyBytes;
if(Objects.isEmpty(keyBytes) && signingKeyResolver !=null) {//use the signingKeyResolver
if(claims !=null) {
key = signingKeyResolver.resolveSigningKey(jwsHeader, claims);
}else{
key = signingKeyResolver.resolveSigningKey(jwsHeader, payload);
}
}
if(!Objects.isEmpty(keyBytes)) {
Assert.isTrue(algorithm.isHmac(),
"Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.");
key =newSecretKeySpec(keyBytes, algorithm.getJcaName());
}
}
Assert.notNull(key, "A signing key must be specified if the specified JWT is digitally signed.");
//re-create the jwt part without the signature. This is what needs to be signed for verification:
String jwtWithoutSignature = base64UrlEncodedHeader +SEPARATOR_CHAR;
if(base64UrlEncodedPayload !=null) {
jwtWithoutSignature += base64UrlEncodedPayload;
}
JwtSignatureValidatorvalidator;
try{
algorithm.assertValidVerificationKey(key);//since 0.10.0:https://github.com/jwtk/jjwt/issues/334
validator = createSignatureValidator(algorithm, key);
}catch(WeakKeyException e) {
throwe;
}catch(InvalidKeyException | IllegalArgumentException e) {
String algName = algorithm.getValue();
String msg = "The parsed JWT indicates it was signed with the " + algName + " signature " +
"algorithm, but the specified signing key of type " + key.getClass().getName() +
" may not be used to validate " + algName + " signatures. Because the specified " +
"signing key reflects a specific and expected algorithm, and the JWT does not reflect " +
"this algorithm, it is likely that the JWT was not expected and therefore should not be " +
"trusted. Another possibility is that the parser was configured with the incorrect " +
"signing key, but this cannot be assumed for security reasons.";
throw newUnsupportedJwtException(msg, e);
}
if(!validator.isValid(jwtWithoutSignature, base64UrlEncodedDigest)) {
String msg = "JWT signature does not match locally computed signature. JWT validity cannot be " +
"asserted and should not be trusted.";
throw newSignatureException(msg);
}
}
final booleanallowSkew =this.allowedClockSkewMillis > 0;
//since 0.3:
if(claims !=null) {
finalDate now =this.clock.now();
longnowTime = now.getTime();
//https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.4
//token MUST NOT be accepted on or after any specified exp time:
Date exp = claims.getExpiration();
if(exp !=null) {
longmaxTime = nowTime -this.allowedClockSkewMillis;
Date max = allowSkew ?newDate(maxTime) : now;
if(max.after(exp)) {
String expVal = DateFormats.formatIso8601(exp,false);
String nowVal = DateFormats.formatIso8601(now,false);
longdifferenceMillis = maxTime - exp.getTime();
String msg = "JWT expired at " + expVal + ". Current time: " + nowVal + ", a difference of " +
differenceMillis + " milliseconds. Allowed clock skew: " +
this.allowedClockSkewMillis + " milliseconds.";
throw newExpiredJwtException(header, claims, msg);
}
}
//https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.5
//token MUST NOT be accepted before any specified nbf time:
Date nbf = claims.getNotBefore();
if(nbf !=null) {
longminTime = nowTime +this.allowedClockSkewMillis;
Date min = allowSkew ?newDate(minTime) : now;
if(min.before(nbf)) {
String nbfVal = DateFormats.formatIso8601(nbf,false);
String nowVal = DateFormats.formatIso8601(now,false);
longdifferenceMillis = nbf.getTime() - minTime;
String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal +
", a difference of " +
differenceMillis + " milliseconds. Allowed clock skew: " +
this.allowedClockSkewMillis + " milliseconds.";
throw newPrematureJwtException(header, claims, msg);
}
}
validateExpectedClaims(header, claims);
}
Object body = claims !=null? claims : payload;
if(base64UrlEncodedDigest !=null) {
return newDefaultJws<>((JwsHeader) header, body, base64UrlEncodedDigest);
}else{
return newDefaultJwt<>(header, body);
}
}
Plain Text
볡μ¬