Java使用nimbus-jose-jwt解析Firebase JWT获取过期时间报typ不允许错误
问题描述
- 需求:在Java环境中解析Firebase令牌,提取
exp字段对应的值,获取令牌过期日期。 - 实现尝试:引入
com.nimbusds:nimbus-jose-jwt:9.23依赖,参考Nimbus JOSE JWT的JWT访问令牌校验示例编写解析代码,已将JWK源地址替换为Firebase对应的谷歌公钥地址,但运行失败。
原实现代码:
String accessToken = "..."; // Create a JWT processor for the access tokens ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>(); // Set the required "typ" header "at+jwt" for access tokens issued by the // Connect2id server, may not be set by other servers jwtProcessor.setJWSTypeVerifier( new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("at+jwt"))); // The public RSA keys to validate the signatures will be sourced from the // OAuth 2.0 server's JWK set, published at a well-known URL. The RemoteJWKSet // object caches the retrieved keys to speed up subsequent look-ups and can // also handle key-rollover // I changed it to what I think should work for firebase, but it doesn't seem to matter what I put here: JWKSource<SecurityContext> keySource = new RemoteJWKSet<>(new URL("https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com")); // The expected JWS algorithm of the access tokens (agreed out-of-band) JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256; // Configure the JWT processor with a key selector to feed matching public // RSA keys sourced from the JWK set URL JWSKeySelector<SecurityContext> keySelector = new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); jwtProcessor.setJWSKeySelector(keySelector); // Set the required JWT claims for access tokens issued by the Connect2id // server, may differ with other servers jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier( null, new HashSet<>(Arrays.asList("exp")))); // Process the token SecurityContext ctx = null; // optional context parameter, not required here JWTClaimsSet claimsSet = jwtProcessor.process(accessToken, ctx); // Print out the token claims set System.out.println(claimsSet.toJSONObject());
运行抛出的错误信息:
JOSE header "typ" (type) "JWT" not allowed com.nimbusds.jose.proc.BadJOSEException: JOSE header "typ" (type) "JWT" not allowed at com.nimbusds.jose.proc.DefaultJOSEObjectTypeVerifier.verify(DefaultJOSEObjectTypeVerifier.java:149) at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:341) at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:303) at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:294)
故障原因
- 代码中设置的
typ头部校验规则只允许值为at+jwt的令牌,但Firebase签发的身份令牌头部typ字段固定值为JWT,直接触发类型校验不通过的异常。 - 配置的公钥地址返回的是X509证书格式内容,不是
RemoteJWKSet要求的标准JWK集合格式,就算修复了typ校验问题,后续也会因为密钥格式不匹配解析失败。
修复方案
调整两处配置即可正常解析:
- 替换
typ校验规则,允许JWT类型的令牌通过校验 - 将公钥源替换为Firebase官方提供的标准JWK Set地址:
https://www.googleapis.com/robot/v1/metadata/jwk/securetoken@system.gserviceaccount.com
修复后的可运行代码如下:
String accessToken = "..."; ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>(); // 适配Firebase令牌的typ值为JWT jwtProcessor.setJWSTypeVerifier( new DefaultJOSEObjectTypeVerifier<>(JOSEObjectType.JWT)); // 替换为Firebase标准JWK地址 JWKSource<SecurityContext> keySource = new RemoteJWKSet<>(new URL("https://www.googleapis.com/robot/v1/metadata/jwk/securetoken@system.gserviceaccount.com")); JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256; JWSKeySelector<SecurityContext> keySelector = new JWSVerificationKeySelector<>(expectedJWSAlg, keySource); jwtProcessor.setJWSKeySelector(keySelector); // 配置需要校验的必填字段 jwtProcessor.setJWTClaimsSetVerifier(new DefaultJWTClaimsVerifier( null, new HashSet<>(Arrays.asList("exp", "iat", "sub", "aud")))); SecurityContext ctx = null; JWTClaimsSet claimsSet = jwtProcessor.process(accessToken, ctx); // 直接获取过期时间 Date expireTime = claimsSet.getExpirationTime(); System.out.println("令牌过期时间:" + expireTime);
如果仅需要提取exp字段不需要做签名合法性校验,可以直接用SignedJWT.parse(accessToken).getJWTClaimsSet().getExpirationTime()获取,不需要配置公钥源和校验规则,但生产环境强烈建议完成完整的签名校验,避免伪造令牌带来的安全风险。
内容的提问来源于stack exchange,提问作者Gavriel
相关产品推荐
相关产品推荐

