11

我已成功安装并运行名为 DriveCommandLine 的 Google Drive 快速启动应用程序。我还对其进行了一些调整,以获取我的云端硬盘帐户中的一个文件的文件信息。

我现在想做的是以某种方式保存凭据并重新使用它们,而用户不必每次都访问网页来获取授权码。我已查看此页面,其中包含有关检索和使用 OAuth 2.0 凭据的说明。为了使用示例类 (MyClass),我修改了 DriveCommandLine 中实例化 Credential 对象的行:

Credential credential = MyClass.getCredentials(code, "");

这会导致引发以下异常:

java.lang.NullPointerException
    at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:187)
    at com.google.api.client.json.jackson.JacksonFactory.createJsonParser(JacksonFactory.java:84)
    at com.google.api.client.json.JsonFactory.fromInputStream(JsonFactory.java:247)
    at com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.load(GoogleClientSecrets.java:168)
    at googledrive.MyClass.getFlow(MyClass.java:145)
    at googledrive.MyClass.exchangeCode(MyClass.java:166)
    at googledrive.MyClass.getCredentials(MyClass.java:239)
    at googledrive.DriveCommandLine.<init>(DriveCommandLine.java:56)
    at googledrive.DriveCommandLine.main(DriveCommandLine.java:115)

我已经研究了这些 API(Google Drive 和 OAuth)2 天了,但进展甚微。对于上述错误以及一般获取持久凭据的问题,我真的很感激。

整个结构对我来说似乎是不必要的复杂。有人愿意解释为什么我不能通过传入我的 Google 用户名和密码来创建一个简单的 Credential 对象吗?

谢谢,Brian O Carroll,爱尔兰都柏林

* 更新 *

好的,我刚刚解决了上述错误,现在我有了一个新错误。

我解决第一个问题的方法是修改 MyClass.getFlow()。我没有从 json 文件创建 GoogleClientServices 对象,而是使用了不同版本的 GoogleAuthorizationCodeFlow.Builder,它允许您直接以字符串形式输入客户端 ID 和客户端密码:

flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, jsonFactory, "<MY CLIENT ID>", "<MY CLIENT SECRET>", SCOPES).setAccessType("offline").setApprovalPrompt("force").build();

我现在遇到的问题是,当我尝试使用流(GoogleAuthorizationCodeFlow 对象)交换 Credentials 对象的授权代码时出现以下错误:

An error occurred: com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
  "error" : "invalid_scope"
}
googledrive.MyClass$CodeExchangeException
        at googledrive.MyClass.exchangeCode(MyClass.java:185)
        at googledrive.MyClass.getCredentials(MyClass.java:262)
        at googledrive.DriveCommandLine.<init>(DriveCommandLine.java:56)
        at googledrive.DriveCommandLine.main(DriveCommandLine.java:115)

我应该为此使用其他范围吗?我目前正在使用 MyClass 提供的范围数组:

private static final List<String> SCOPES = Arrays.asList(
        "https://www.googleapis.com/auth/drive.file",
        "https://www.googleapis.com/auth/userinfo.email",
        "https://www.googleapis.com/auth/userinfo.profile");

谢谢!

4

3 回答 3

13

我感觉到你的痛苦。我已经两个月了,仍然感到惊讶。

我的一些学习...

  • 当您请求用户权限时,请指定“offline=true”。这将(“有时”原文如此)返回一个刷新令牌,它与具有受限权限的密码一样好。您可以存储它并随时重复使用它(直到用户撤销它)以获取访问令牌。

  • 我的感觉是,Google SDK 与其说是帮助,不如说是阻碍。一个一个,我已经停止使用它们,现在直接调用 REST API。

  • 最后一点,您可以(仅)使用 Google 客户端登录协议来访问上一代 API。但是,这已完全弃用,不久将被关闭。OAuth 旨在对本质上复杂的授权进行细粒度控制。因此,尽管我同意这很复杂,但我认为这不是不必要的。我们生活在一个复杂的世界:-)

你和我的经验表明,开发社区仍然需要一个整合的文档和方法来将这些东西放入我们的后视镜中,这样我们就可以专注于手头的任务。

于 2012-09-18T15:15:02.550 回答
3

Oath2Scopes 导入如下:

import com.google.api.services.oauth2.Oauth2Scopes;

您需要在类路径中有 jar 文件“google-api-services-oauth2-v2-rev15-1.8.0-beta.jar”才能访问该包。可以在这里下载。

不,我不知道如何在不必访问授权 URL 至少一次并复制代码的情况下获取凭据。我已经修改了MyClass以从数据库中存储和检索凭据(在我的例子中,它是一个包含 userid、accesstoken 和 refreshtoken 的简单表)。这样,我只需获取一次授权代码,一旦获得访问/刷新令牌,我就可以重用它们来制作 GoogleCredential 对象。以下是我制作 GoogleCredential 对象的方法:

GoogleCredential credential = new GoogleCredential.Builder().setJsonFactory(jsonFactory)
            .setTransport(httpTransport).setClientSecrets(clientid, clientsecret).build();
    credential.setAccessToken(accessToken);
    credential.setRefreshToken(refreshToken);

只需在上面输入您的 clientid、clientsecret、accessToken 和 refreshToken。

我真的没有太多时间来分离和整理我的整个代码以将其发布在这里,但如果您仍然遇到问题,请告诉我,我会看看我能做些什么。虽然,您实际上是在向盲人问路。我对整个系统的理解非常粗略!

干杯,布赖恩

于 2012-09-21T08:45:26.930 回答
1

好的,我终于解决了上面的第二个问题,我终于得到了一个带有访问令牌和刷新令牌的工作 GoogleCredential 对象。

我一直试图通过修改MyClass(管理凭据的那个)中的范围列表来解决范围问题。最后,我需要在我的DriveCommandLine修改版本(最初用于获取授权码的版本)中调整范围。我从 Oauth2Scopes 添加了 2 个作用域:

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
    httpTransport, jsonFactory, CLIENT_ID, CLIENT_SECRET,
    Arrays.asList(DriveScopes.DRIVE, Oauth2Scopes.USERINFO_EMAIL, Oauth2Scopes.USERINFO_PROFILE))
    .setAccessType("offline").setApprovalPrompt("force").build();

添加用户信息的范围允许我稍后在 MyClass 中获取用户 ID。我现在可以使用用户 ID 将凭据存储在数据库中以供重复使用(无需让用户每次都访问 URL)。我还按照 pinoyyid 的建议将访问类型设置为“离线”。

于 2012-09-19T11:56:11.163 回答