You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
故障原因
  1. 代码中设置的typ头部校验规则只允许值为at+jwt的令牌,但Firebase签发的身份令牌头部typ字段固定值为JWT,直接触发类型校验不通过的异常。
  2. 配置的公钥地址返回的是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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 01:30:27