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&theme=outline&text=sign_in_with&shape=rectangular&logo_alignment=left&width=269&client_id=******.apps.googleusercontent.com&iframe_id=gsi_86518_300486&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
相关产品推荐
相关产品推荐

