java - 谷歌 JWT 无效签名
问题描述
我正在尝试使用过滤器验证后端中的 Bearer jwt 令牌(从 jwt 检索的用户未注册,我创建一个新的,如果已注册,则允许使用 API)。为了实现这一点,我正在使用 Google OAuth2 服务(并计划使用 Facebook 和 Twitter 以及“社交登录”),但我无法验证令牌。
为了验证令牌,我首先尝试使用 Google 的 GoogleIdTokenVerifier (如此处所述),但据报道我使用的令牌具有无效签名(当我使用 google OAuth2 服务获取它们时我发现这很奇怪),所以我尝试验证他们自己访问谷歌使用的公共签名并使用Auth0 JWT 库。我再次发现自己遇到了上一期(签名无效)。
所以 2 次尝试和 2 次失败,我认为令牌是问题,我使用在线 google api 服务来验证它(https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=)。事实证明,令牌不是问题,所以我又回到零。
最后,我解码了令牌并将令牌上使用的公钥(由 jwt.io报告)与我使用谷歌签名生成的公钥进行比较,发现它们不匹配。
令牌公钥:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkoT6VAUmOwqXQoyMQkk0
F1JJd81H7ksx7FHfKtqXvdrDt9LLr1IDJ+CFpbbn4SuJeYQcUo+lHA1/vbbtgCBS
yd/H86WGGARlPrsqFF+hbBBauhUQuXMxFxJKAiQS0WIbFvDkwRaNiGIMYQvzwDKb
ms5tCqZ04T5Qez9v64i3RlU8+upJxG9duzeXQjrnC5uJeeG+9fwE06RRZ1Y8Uul6
3Lpxicw5Alyd5HQIzF5vSSwjqdVrBUJAToxuTIqIHR24omrz1f7Jf97wy2U7KUoS
nytauyaZtph7RmsUPVMFr9fGMxNVU8tKEVeOYJv+piOc0gfPvIahbv2en0DhOax6
OQIDAQAB
-----END PUBLIC KEY-----
生成的公钥(使用谷歌提供的签名)
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqWaspOi9jHSmTMBq8EOP
XJDIkqXdSfb+VeEczodUetDIuA0/q5muW1YTcbmwzJwu9WxSi3ZOuRe1zjmP1HyT
z6ffnMFvwOrtN/pBahn++QYOIXlgw3SU4WtWn+VIRbuAVya+XxE0jfekwuuvtt3f
75WejmUbEWch6546YSu/57qM6IkOkJjxmu7rwcBuVruwPxL5IrGkZarEmv3wuYf5
trqZrjYXcd0O3/dzikFmUsel5DVR/y86i89lkl2ofL+netLNKC3edsMiKzXJExiC
0fu2lyLuf6IfM0MKcdwQS2m4JPmKtPh+s+wV9OtvWslHf+aVHCc+YRdhlARfN8o0
mwIDAQAB
-----END PUBLIC KEY-----
我真的怀疑谷歌是否更改了他们的 OAuth2 签名公钥并忘记将它们更新给公众。
我可能做错了什么吗?
下面我提供了用于检索密钥并使用它们来验证 JWT 令牌的代码。
公共证书检索和公钥生成:
final Mono<GoogleJWTDto> googleJWTDtoMono = webClient
.get()
.retrieve()
.bodyToMono(GoogleJWTDto.class);
googleJWTDtoMono.subscribe((googleJWTDto) -> {
for(final GoogleJWT googleJWT : googleJWTDto.keys) {
try {
final RSAKey rsaKey = new RSAKey.Builder(Base64URL.from(googleJWT.n), Base64URL.from(googleJWT.e))
.keyID(googleJWT.kid)
.keyUse(KeyUse.parse(googleJWT.use))
.build();
rsaKeys.put(googleJWT.kid, rsaKey);
} catch (ParseException e) {
LOGGER.error("the key (kid: {} from: {}) could not be loaded", googleJWT.kid, "google");
}
}
});
验证密钥选择:
final DecodedJWT decodedJWT = JWT.decode(token);
final RSAKey rsaKey = getRSAKey(decodedJWT.getKeyId());
final boolean isValid = JWTUtils.verifyToken(decodedJWT, rsaKey);
JWTUtils 类:
public class JWTUtils {
public static boolean verifyToken(final DecodedJWT decodedJWT, final RSAKey key) {
try {
final Algorithm algorithm = getTokenAlgorithm(decodedJWT, key);
final JWTVerifier verification = JWT.require(algorithm)
.withIssuer(decodedJWT.getIssuer())
.build();
verification.verify(decodedJWT);
return true;
} catch (JWTVerificationException | JOSEException e) {
return false;
}
}
private static Algorithm getTokenAlgorithm(final DecodedJWT decodedJWT, final RSAKey key) throws JOSEException {
switch (decodedJWT.getAlgorithm()) {
case "RS256":
return Algorithm.RSA256(key.toRSAPublicKey(), null);
default:
throw new IllegalArgumentException("the algorithm '" + decodedJWT.getAlgorithm() + "' is not supported");
}
}
}
PS:我知道我可以使用在线服务来验证 jwt,但由于 jwt 的全部目的是能够离线验证它们(使用公钥),我想避免这种方法。
解决方案
推荐阅读
- macos - 在 macOS >= 10.13 中检索 ssh 密码
- ios - 如何使用 Superpowered 库播放多种声音
- c++ - 本地 C++ 插件 InputStream 的可读节点流
- django - 使用 pre_save 信号编辑上传的文件(djangos FileField)
- wpf - WPF中Tab和Ctrl-Tab的区别
- elasticsearch - ONGRFilterBundle - 动态聚合限制过滤器?
- ubuntu - 谁杀死了我的进程?崩溃生成 SIGHUP?
- javascript - 设置 Slate.js
- sql - 是否可以在不首先更新事务的情况下对事务中的表/行进行读锁定?
- datastax-enterprise - 如何在 gremlin-console 中显示图形的复制因子?