如何使用 gmail api 获取访问令牌

Posted

技术标签:

【中文标题】如何使用 gmail api 获取访问令牌【英文标题】:How to get access token using gmail api 【发布时间】:2016-07-12 21:59:05 【问题描述】:

我得到了this document之后的授权码。但是当我尝试获取访问令牌时,我总是遇到错误。谁能帮帮我?

public String AccessToken()

    String accessToken = "";
    StringBuilder strBuild = new StringBuilder();

    String authURL = "https://accounts.google.com/o/oauth2/token?";
    String code = "4/SVisuz_x*********************";
    String client_id = "******************e.apps.googleusercontent.com";
    String client_secret = "*******************";
    String redirect_uri = "urn:ietf:wg:oauth:2.0:oob";
    String grant_type="authorization_code";
    strBuild.append("code=").append(code)
            .append("&client_id=").append(client_id)
            .append("&client_secret=").append(client_secret)
            .append("&redirect_uri=").append(redirect_uri)
            .append("&grant_type=").append(grant_type);
    System.out.println(strBuild.toString());
    try
        URL obj = new URL(authURL);
        HttpURLConnection con = (HttpURLConnection) obj.openConnection();
        con.setDoOutput(true);
        con.setRequestMethod("POST");

        con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        con.setRequestProperty("Host", "www.googleapis.com");

        //BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(con.getOutputStream()));
        //bw.write(strBuild.toString());
        //bw.close();

        DataOutputStream wr = new DataOutputStream(con.getOutputStream());
        wr.writeBytes(strBuild.toString());
        wr.flush();
        wr.close();

        //OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
        System.out.println(con.getResponseCode());
        System.out.println(con.getResponseMessage());             

     catch (Exception e)
    
        System.out.println("Error.");
    
    return "";

当我运行这段代码时,输​​出是: 400 Bad Request

【问题讨论】:

你得到什么错误? @JayLee 输出是一个字:Error 您是否在输出中看到消息“无效的字符串值”? @tqjustc 您能否从您的 catch 块中提供 e.printStackTrace(); ?这将有助于为您提供适当的解决方案。 @SkyWalker 它仍然打印:400,错误请求 【参考方案1】:

如何使用 gmail api 获取访问令牌?

Ans: 根据your following tutorial,您使用的是OAuth 2.0。所以有一个使用OAuth 2.0 访问Google API 的基本模式。它遵循 4 个步骤:

    从 Google Developers Console 获取 OAuth 2.0 凭据。 从 Google 授权服务器获取访问令牌。 将访问令牌发送到 API。 如有必要,请刷新访问令牌。

详情可以关注教程-Using OAuth 2.0 to Access Google APIs

您必须访问 Google Developers Console 以获取 OAuth 2.0 凭据,例如 Google 和您的应用程序都知道的 client IDclient secret


根本原因分析:

问题 1:

研究了你的代码,发现有些不足。如果你的代码运行顺利,那么代码总是给出一个空字符串。因为你的AccessToken() 方法总是返回return "";

问题 2:

 catch (Exception e)
    
        System.out.println("Error.");
    

你的 try catch 块是异常块。因为,您似乎没有正确完成您的代码。您错过了encoding 以及使用准备访问令牌的JSONObject。所以它给出的输出为

错误。

解决办法:

我知道你的代码与tutorial类似

因为您的代码需要更多更改来解决您的问题。所以我建议你使用 LinkedHashMapArrayList。这些将提供更简单的解决方案。所以我给你2个示例代码,让你的生活更轻松。您可以选择其中任何一个。您需要将refresh_token, client id, client secret and grant type 更改为您的。

private String getAccessToken()

    try
    
        Map<String,Object> params = new LinkedHashMap<>();
        params.put("grant_type","refresh_token");
        params.put("client_id",[YOUR CLIENT ID]);
        params.put("client_secret",[YOUR CLIENT SECRET]);
        params.put("refresh_token",[YOUR REFRESH TOKEN]);

        StringBuilder postData = new StringBuilder();
        for(Map.Entry<String,Object> param : params.entrySet())
        
            if(postData.length() != 0)
            
                postData.append('&');
            
            postData.append(URLEncoder.encode(param.getKey(),"UTF-8"));
            postData.append('=');
            postData.append(URLEncoder.encode(String.valueOf(param.getValue()),"UTF-8"));
        
        byte[] postDataBytes = postData.toString().getBytes("UTF-8");

        URL url = new URL("https://accounts.google.com/o/oauth2/token");
        HttpURLConnection con = (HttpURLConnection)url.openConnection();
        con.setDoOutput(true);
        con.setUseCaches(false);
        con.setRequestMethod("POST");
        con.getOutputStream().write(postDataBytes);

        BufferedReader  reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
        StringBuffer buffer = new StringBuffer();
        for (String line = reader.readLine(); line != null; line = reader.readLine())
        
            buffer.append(line);
        

        JSONObject json = new JSONObject(buffer.toString());
        String accessToken = json.getString("access_token");
        return accessToken;
    
    catch (Exception ex)
    
        ex.printStackTrace(); 
    
    return null;

访问google play android developer api,需要通过 上一次刷新令牌以获取访问令牌

private String getAccessToken(String refreshToken)

HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
try 

    List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
    nameValuePairs.add(new BasicNameValuePair("grant_type",    "refresh_token"));
    nameValuePairs.add(new BasicNameValuePair("client_id",     GOOGLE_CLIENT_ID));
    nameValuePairs.add(new BasicNameValuePair("client_secret", GOOGLE_CLIENT_SECRET));
    nameValuePairs.add(new BasicNameValuePair("refresh_token", refreshToken));
    post.setEntity(new UrlEncodedFormEntity(nameValuePairs));

    org.apache.http.HttpResponse response = client.execute(post);
    BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
    StringBuffer buffer = new StringBuffer();
    for (String line = reader.readLine(); line != null; line = reader.readLine())
    
        buffer.append(line);
    

    JSONObject json = new JSONObject(buffer.toString());
    String accessToken = json.getString("access_token");

    return accessToken;


catch (IOException e)  e.printStackTrace(); 

return null;

资源链接:

Unable to get the subscription information from Google Play android Developer API

Using java.net.URLConnection to fire and handle HTTP requests

How to send HTTP request GET/POST in Java

希望samplesresource link 能帮助您解决问题并获得access token 的访问权限。


What is 400 bad request?

Ans:表示查询无效。缺少父 ID,或者请求的维度或指标组合无效。

推荐操作:您需要对 API 查询进行更改才能使其正常工作。

HTTP/1.1 400 Bad Request error可以转my another answer。 它将帮助您了解which host you need to use 以及您需要申请哪些条件

Why token expires? What is the limit of token?

令牌可能由于以下原因之一停止工作:

    用户拥有revoked 访问权限。 令牌尚未用于six monthsuser changed passwords 和令牌包含 Gmail、日历、 联系人或环聊范围。 用户帐号有exceeded a certain number of token requests

currently a limit of 25 refresh tokens per user account per client。如果达到限制,创建新令牌会自动使最旧的令牌无效,而不会发出警告。此限制不适用于服务帐号。

应遵循哪些预防措施?

注意事项 - 1:

某些请求需要用户登录的身份验证步骤 使用他们的 Google 帐户。登录后询问用户是否 他们愿意授予您的应用程序的权限 请求。这个过程称为用户同意。

如果用户授予权限,Google 授权服务器 向您的应用程序发送访问令牌(或授权码 您的应用程序可用于获取访问令牌)。如果用户这样做 不授予权限,服务器返回错误。

注意事项 - 2:

如果为 Google+ API 颁发访问令牌,它不会授予 访问 Google 通讯录 API。但是,您可以发送该访问权限 多次向 Google+ API 发送令牌以进行类似操作。

注意事项 - 3:

访问令牌的有效期通常为 1 小时,之后 如果你尝试使用它,你会得到一个错误。 Google Credential 负责自动“刷新”令牌,这意味着 获取新的访问令牌。

将刷新令牌保存在安全的长期存储中并继续使用 只要它们仍然有效。限制适用于数量 刷新每个客户端-用户组合发出的令牌,并且每个 跨所有客户端的用户,并且这些限制是不同的。如果你的 应用程序请求足够的刷新令牌来遍历其中一个 限制,旧的刷新令牌停止工作。

【讨论】:

现在的问题是我什至无法打印出 http 消息。它总是出错。我检查了你的另一个答案。但我仍然无法得到正确答案 @tqjustc 我已经用一些示例和资源链接更新了我的答案。请检查并向我提供更新,以便我可以给您更好的反馈。【参考方案2】:

您没有使用正确的端点。尝试将authURL更改为https://www.googleapis.com/oauth2/v4/token

来自文档:

要发出此令牌请求,请将 HTTP POST 请求发送到 /oauth2/v4/token 端点

实际的请求可能如下所示:

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/v6xr77ewYqhvHSyW6UJ1w7jKwAzu&
client_id=8819981768.apps.googleusercontent.com&
client_secret=your_client_secret&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code

参考https://developers.google.com/identity/protocols/OAuth2InstalledApp#handlingtheresponse

【讨论】:

@newhourse 我仍然得到同样的错误输出:400 Bad Request 尝试使用URLEncoder.encode(param, "UTF-8");对参数进行编码【参考方案3】:

对我来说,您的请求很好,我使用 Curl 进行了尝试,我还收到了一个“HTTP/1.1 400 Bad Request”,原因是它失败了“invalid_grant”:

curl -X POST https://www.googleapis.com/oauth2/v4/token -d 'code=4/SVisuz_x*********************&client_id=*******************7vet.apps.googleusercontent.com&client_secret=***************&redirect_uri=https://oauth2-login-demo.appspot.com/code&grant_type=authorization_code'

我收到(HTTP/1.1 400 错误请求):


 "error": "invalid_grant",
 "error_description": "Code was already redeemed."


现在使用 Apache 的 HttpClient :

URL obj = new URL(authURL);
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(authURL);
post.addHeader("Content-Type", "application/x-www-form-urlencoded");
post.addHeader("Host", "www.googleapis.com");
post.setEntity(new StringEntity(strBuild.toString()));

HttpResponse resp = client.execute(post);
System.out.println(resp.getStatusLine());
System.out.println(EntityUtils.toString(resp.getEntity()));

我在控制台中看到:

HTTP/1.1 400 Bad Request

 "error": "invalid_grant",
 "error_description": "Code was already redeemed."


您确定您使用的代码仍然有效吗?可以试试新的吗?

【讨论】:

【参考方案4】:

首先,你必须看看这个页面:

https://developers.google.com/gmail/api/auth/web-server#create_a_client_id_and_client_secret

您在查询参数代码中看到的值是一个字符串,您必须发布到 google 才能获取访问令牌。

Web 服务器收到授权码后,可以用授权码交换访问令牌和刷新令牌。此请求是对 URL https://www.googleapis.com/oauth2/v3/token 的 HTTPS POST POST /oauth2/v3/token HTTP/1.1 内容类型:application/x-www-form-urlencoded

code=4/v4-CqVXkhiTkn9uapv6V0iqUmelHNnbLRr1EbErzkQw#&redirect_uri=&client_id=&scope=&client_secret=************&grant_type=authorization_code https://developers.google.com/identity/protocols/OAuth2WebServer

【讨论】:

【参考方案5】:

我想我明白出了什么问题:

    正如@newhouse 所说,你应该发帖到https://www.googleapis.com/oauth2/v4/token 而不是https://accounts.google.com/o/oauth2/token(@newhouse 我给了你一个 +1 :))

    https://www.googleapis.com/oauth2/v4/token 用于获取authorization_codehttps://accounts.google.com/o/oauth2/token 用于获取code)。

    您不能多次使用同一个code

    其他一切似乎都井井有条,因此,如果您不断获得 400,您可能会尝试使用多次获得的 code(然后您将一次又一次地获得 400)。

* 你也应该丢掉con.setRequestProperty("Host", "www.googleapis.com");

【讨论】:

当您说accounts.google.com/o/oauth2/token 用于获取代码时,“代码”是访问令牌吗?同样在两个请求 googleapis.com/oauth2/v4/token 和 accounts.google.com/o/oauth2/token 之后,是否会有第三个请求发送电子邮件内容以实际发送电子邮件?此链接developers.google.com/identity/protocols/oauth2 中的哪个场景图是我们使用此 gmail api 发送电子邮件所需遵循的完整交换?【参考方案6】:

参考:https://developers.google.com/android-publisher/authorization

您已经拥有称为“刷新令牌”的授权代码。请妥善保管。您可以使用“刷新令牌”来生成“访问令牌”。

要获取“访问令牌”,请向以下 URL 发出 post 请求

https://accounts.google.com/o/oauth2/token

参数:

grant_type client_id client_secret 刷新令牌

其中“grant_type”应该是“refresh_token”

我们使用 php 来做同样的事情,这里是 PHP 的代码供您参考

    $curl = curl_init();

    curl_setopt_array($curl, array(
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => 'https://accounts.google.com/o/oauth2/token',
    CURLOPT_USERAGENT => 'Pocket Experts Services',
    CURLOPT_POST => 1,
    CURLOPT_POSTFIELDS => array(
    "grant_type" => "refresh_token",
    "client_id" => $GOOGLE_CLIENT_ID,
    "client_secret" => $GOOGLE_CLIENT_SECRET,
    "refresh_token" => $GOOGLE_REFRESH_TOKEN,
    )));


    // Send the request & save response to $resp
    $resp = curl_exec($curl);

希望对你有所帮助。

【讨论】:

【参考方案7】:

低安全性方法是暂时的,我无法在生产中使用它,但我发现一篇文章让使用节点here 变得更容易 使用示例代码,它可以完美运行

【讨论】:

以上是关于如何使用 gmail api 获取访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何获取虚拟谷歌访问令牌来测试 oauth google api

如何使用带有 OAuth 令牌的 Asp.Net MVC 中的 gmail API 更快地获取日期明智的电子邮件

在 chrome 扩展中,如何从当前 chrome 用户以外的 gmail 用户获取访问令牌?

使用 OAuth 2.0 访问令牌访问 Gmail Imap

如何使用 Green Button API 获取访问令牌?

客户端未经授权使用此方法检索访问令牌 Gmail API C#