Google登录集成遇HTTP 400错误:code传递及页面跳转问题咨询
实现Google登录时遇到HTTP 400错误的解决思路
问题说明
使用Google新库实现登录功能时,Servlet调用https://accounts.google.com/o/oauth2/token接口抛出异常:
java.io.IOException: Server returned HTTP response code: 400 for URL: https://accounts.google.com/o/oauth2/token
同时需要完成认证后将用户重定向到项目google_login_to_website的success.jsp页面,确认传递的code值是否正确,作为新手需要相关解决思路。
代码实现
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html> <html> <head> <meta charset="ISO-8859-1"> <title>Sign In</title> <script src="https://accounts.google.com/gsi/client" async defer></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> </script> <script> function handleCredentialResponse(response) { var JwtToken=response.credential; //var parse= JSON.parse(atob(JwtToken.split('.')[1])); //var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri); //var email=parse.email; //var name=parse.name; var redirectUrl = 'oauth2callback'; //console.log(g_id_onload.id); var form = $('<form action="' + redirectUrl + '" method="post">' +'<input type="text" name="code" value="' +JwtToken + '" />' +'</form>'); $('body').append(form); form.submit(); } </script> </head> <body> <div class="login-wrap"> <div class="login-html"> <input id="tab-1" type="radio" name="tab" class="sign-in" checked><label for="tab-1" class="tab">Sign In</label> <div class="login-form"> <form method="post" action="login"> <div class="sign-in-htm"> <div class="group"> <br><br><label class="label">Username</label> <input name="uname" type="text" class="input"> </div> <div class="group"> <label class="label">Password</label> <input name="pwd" type="password" class="input" data-type="password"> </div> <div class="group"> <input type="submit" class="button" value="Sign In"> </div> <div class="hr"></div> <div id="g_id_onload" data-client_id="1067577993694-8643nojsg1bl3l7adfdevc9gulj539dh.apps.googleusercontent.com" data-callback="handleCredentialResponse" data-auto_prompt="false"> </div> <div class="g_id_signin" data-type="standard"></div> </div></form> </div> </div> </div> </body> </html>
oauth2callback.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL; import java.net.URLConnection; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @WebServlet("/Oauth2callback") public class Oauth2callback extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("entering doGet"); try { // get code String code = request.getParameter("code"); // format parameters to post String urlParameters = "code=" + code + "&client_id=1067577993694-8643nojsg1bl3l7adfdevc9gulj539dh.apps.googleusercontent.com" + "&client_secret=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" + "&redirect_uri=http://localhost:8081/google_login_to_website/oauth2callback" +"&grant_type=authorization_code"; System.out.println(urlParameters); //post parameters URL url = new URL("https://accounts.google.com/o/oauth2/token"); HttpURLConnection urlConn =(HttpURLConnection) url.openConnection(); urlConn.setDoOutput(true); OutputStreamWriter writer = new OutputStreamWriter(urlConn.getOutputStream()); writer.write(urlParameters); writer.flush(); //get output in outputString String line, outputString = ""; BufferedReader reader = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); while ((line = reader.readLine()) != null) { outputString += line; } System.out.println(outputString); //get Access Token // JsonObject json = (JsonObject)new JsonParser().parse(outputString); JsonObject json = JsonParser.parseString(outputString).getAsJsonObject(); String access_token = json.get("access_token").getAsString(); System.out.println(access_token); //get User Info url = new URL("https://www.googleapis.com/oauth2/v1/userinfo?access_token="+ access_token); urlConn = (HttpURLConnection) url.openConnection(); outputString = ""; reader = new BufferedReader(new InputStreamReader( urlConn.getInputStream())); while ((line = reader.readLine()) != null) { outputString += line; } System.out.println(outputString); // Convert JSON response into Pojo class GooglePojo data = new Gson().fromJson(outputString, GooglePojo.class); System.out.println(data); writer.close(); reader.close(); } catch (MalformedURLException e) { System.out.println( e); } catch (ProtocolException e) { System.out.println( e); } System.out.println("leaving doGet"); } }
GooglePojo.java
public class GooglePojo { String id; String email; boolean verified_email; String name; String given_name; String family_name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public boolean isVerified_email() { return verified_email; } public void setVerified_email(boolean verified_email) { this.verified_email = verified_email; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGiven_name() { return given_name; } public void setGiven_name(String given_name) { this.given_name = given_name; } public String getFamily_name() { return family_name; } public void setFamily_name(String family_name) { this.family_name = family_name; } @Override public String toString() { return "GooglePojo [id=" + id + ", email=" + email + ", verified_email=" + verified_email + ", name=" + name + ", given_name=" + given_name + ", family_name=" + family_name + "]"; } }
解决思路
1. 400错误核心原因
你使用的是Google新的GSI(一键登录)库,回调返回的response.credential是JWT格式的ID Token,而非OAuth2授权码流程中的authorization code。但你的Servlet错误地将这个JWT当作授权码去请求token接口,导致接口返回400参数错误。
2. 两种修正方案
方案一:直接解析JWT获取用户信息(推荐,符合GSI设计)
GSI的核心就是返回ID Token,无需再走授权码流程:
- 前端解析:取消注释前端代码中
var parse= JSON.parse(atob(JwtToken.split('.')[1]));,直接从JWT的Payload部分解析出email、name等用户信息,再将这些信息传给后端。 - 后端验证:如果需要确保信息真实有效,可使用Google官方的
google-auth-library-java库验证JWT的签名和合法性,避免伪造。 - 前端回调修改示例:
function handleCredentialResponse(response) { var JwtToken=response.credential; var parse= JSON.parse(atob(JwtToken.split('.')[1])); var redirectUrl = 'oauth2callback'; var form = $('<form action="' + redirectUrl + '" method="post">' + '<input type="hidden" name="email" value="' + parse.email + '" />' + '<input type="hidden" name="name" value="' + parse.name + '" />' + '</form>'); $('body').append(form); form.submit(); }
方案二:切换到授权码流程
如果坚持使用原有Servlet的授权码逻辑,需要修改GSI配置启用授权码模式:
- 在
g_id_onload标签中添加data-response_type="code"和data-scope="openid email profile",此时回调的response.code才是合法的授权码。
3. 重定向到success.jsp实现
在Servlet处理完用户信息验证后,添加重定向代码(需确保在所有输出前调用):
// 在获取并验证用户信息后添加 response.sendRedirect("success.jsp");
4. 额外注意事项
- 不要将
client_secret暴露到前端或代码仓库,仅在后端使用。 - 你参考的旧教程基于老版OAuth2流程,与新GSI库不兼容,建议以Google官方文档为准。
内容的提问来源于stack exchange,提问作者Belle
相关产品推荐
相关产品推荐

