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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 22:24:14