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

Google OAuth 2.0登录返回JWT中nonce缺失问题排查与解决

Google登录JWT缺失nonce字段的排查与解决

最近在做Google登录的JWT验证时踩了个坑,折腾了好一阵才解决——按照OpenID Connect的规范,Google返回的ID Token(也就是JWT)里应该带着我前端传入的nonce,但后端验证时payload.getNonce()一直返回null,其他字段(比如用户姓名、邮箱)却都能正常获取。下面把我的排查过程和解决办法分享出来:

问题重现

前端配置了Google登录按钮并传入随机生成的nonce,后端用Google官方Java SDK验证JWT,却始终拿不到nonce值。

前端代码片段

<google-login class="ng-isolate-scope">
 <div id="g_id_onload" data-client_id="********.apps.googleusercontent.com" data-auto_prompt="true" data-nonce="<randomly generated UUID>" data-login_uri="http://localhost:8080/appName/rest/auth/google/login"></div>
 <div class="g_id_signin" data-type="standard" data-theme="outline" data-text="sign_in_with" data-shape="rectangular" data-logo_alignment="left" data-width="269">
 <div class="S9gUrf-YoZ4jf" style="position: relative;">
 <div></div>
 <iframe src="https://accounts.google.com/gsi/button?type=standard&amp;theme=outline&amp;text=sign_in_with&amp;shape=rectangular&amp;logo_alignment=left&amp;width=269&amp;client_id=******.apps.googleusercontent.com&amp;iframe_id=gsi_86518_300486&amp;as=xHN4SBVSZDA7r8a2zB77gA" id="gsi_86518_300486" title="Sign in with Google Button" style="display: block; position: relative; top: 0px; left: 0px; height: 44px; width: 289px; border: 0px; margin: -2px -10px;"></iframe>
 </div>
 </div>
</google-login>

后端验证代码

Kotlin调用代码

val token: String? = httpServletRequest?.getParameter("g_csrf_token")
val credential: String? = httpServletRequest?.getParameter("credential")
val googleUser = GoogleUser().fromToken(token, credential)

Java核心验证逻辑

public GoogleUser fromToken(String token, String credential){
 GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(
 new NetHttpTransport(), new GsonFactory())
 .setAudience(Collections.singletonList(<my google client id>))
 .build();
 GoogleIdToken verifiedCredential = verifier.verify(credential);
 Payload payload = verifiedCredential.getPayload();
 NonceEntity nonceEntity = nonceService.findByNonce(payload.getNonce()); // 这里payload.getNonce()始终为null
 // ...其他业务逻辑
}

排查过程

我先做了几个关键确认:

  • 后端验证逻辑没问题:JWT本身有效,用户姓名、邮箱等字段都能正常读取
  • nonce确实发送到了Google:通过浏览器「网络」面板查看,gsi/select和ServiceLogin的GET请求里都带着我生成的nonce参数,但返回的credential(JWT)中就是没有该字段

问题根源

最终发现是时序问题:我用的Angular项目中,data-nonce的值是通过AJAX异步请求生成的,但Google的g_id_onload脚本在页面加载时就立刻读取了div的属性——这时候nonce还没被填充进去,导致Google根本没收到我们生成的nonce,自然不会在返回的JWT里包含它。

解决方案

我们有两种可行方案,优先选了更简单的第一种:

  • 方案1:延迟加载Google登录脚本:重写前端JS逻辑,确保只有在AJAX调用完成、nonce值已经填充到g_id_onload的data-nonce属性后,再把Google的登录脚本添加到页面中。这样Google脚本读取属性时,nonce已经存在了。
  • 方案2:服务端生成nonce:把nonce的生成逻辑移到后端控制器,前端从后端获取nonce后再配置Google登录按钮,这种方式也能避免异步加载的时序问题,但需要多一次前后端交互。

内容的提问来源于stack exchange,提问作者Tabs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:52:37