【问题标题】:Google Spreadsheet with a service account带有服务帐户的 Google 电子表格
【发布时间】:2015-07-08 08:55:35
【问题描述】:

我想使用 de java Google 电子表格 API 访问 Google Drive 上的电子表格。过去我使用客户端登录方法登录,但现在已弃用此方法。 现在我正在尝试使用服务帐户在不与用户交互的情况下进行身份验证,因为该工具在后台运行,我需要自动化。但我无法让它工作。这些是我现在的代码。

    List<String> SCOPES_ARRAY = Arrays.asList(
            "https://www.googleapis.com/auth/drive.file",
            "https://www.googleapis.com/auth/userinfo.email",
            "https://www.googleapis.com/auth/userinfo.profile",
            "https://docs.google.com/feeds",
            "https://spreadsheets.google.com/feeds");
    credential = new GoogleCredential.Builder()
                .setTransport(transport)
                .setJsonFactory(jsonFactory)
                .setServiceAccountId(
                        "xxxxxx@developer.gserviceaccount.com")
                .setServiceAccountScopes(SCOPES_ARRAY)
                .setServiceAccountPrivateKeyFromP12File(p12)
                .setServiceAccountUser("me@gmail.com")
                .build();
    service = new SpreadsheetService("MonitorV3");
    service.setOAuth2Credentials(credential);

    SPREADSHEET_FEED_URL = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");

    worksheetFeed = service.getFeed(spreadsheet.getWorksheetFeedUrl(),
                    WorksheetFeed.class);

但是在执行最后一行时我得到了一个空指针异常。刷新令牌出了点问题。有人看到我做错了吗?

我做了一个独立的简单应用程序,现在我收到以下详细错误:

Exception in thread "main" com.google.gdata.util.AuthenticationException: Failed to refresh access token: 401 Unauthorized
at com.google.gdata.client.GoogleAuthTokenFactory$OAuth2Token.refreshToken(GoogleAuthTokenFactory.java:260)
at com.google.gdata.client.GoogleAuthTokenFactory.handleSessionExpiredException(GoogleAuthTokenFactory.java:702)
at com.google.gdata.client.GoogleService.handleSessionExpiredException(GoogleService.java:738)
at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:649)
at com.google.gdata.client.Service.getFeed(Service.java:1017)
at JavaApplication20.printDocuments(JavaApplication20.java:50)
at main.main(main.java:35)

【问题讨论】:

  • 您是否授予服务帐户访问工作表的权限?
  • 感谢您的快速回复。您如何授予服务帐户访问权限?在 serviceaccountuser 中拥有电子邮件的用户有权访问电子表格。我什至与驱动器的所有者一起尝试过,但结果相同。我想我做错了什么或忘记了什么。当我调试线程时。我看到在 getfeed 之后它将进入 sessionExpiredException ,然后在出现空指针的 de refreshtoken 方法中。我需要设置一个refreshtoken还是什么?
  • 获取服务帐户电子邮件地址并像其他用户一样授予它访问权限。
  • 我试过这个但结果相同:nullpointer。我编辑了我的帖子并添加了空指针发生位置的图像

标签: java google-api google-spreadsheet-api


【解决方案1】:

GoogleCredential 目前已弃用。相反,如果您需要使用服务帐户访问 Google 表格,请使用 Google OAuth2 library

使用示例:

// A few common declarations:
private static final String APPLICATION_NAME = "My Google Sheets Application";
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS_READONLY);

// Initializing the service:
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredentials googleCredentials;
try(InputStream inputSteam = <YOURCLASS>.class.getResourceAsStream("/path/to/credentials/file.json")) {
  googleCredentials = GoogleCredentials.fromStream(credentialsStream).createScoped(SCOPES)
}
Sheets service = Sheets.Builder(HTTP_TRANSPORT, JSON_FACTORY, new HttpCredentialsAdapter(googleCredentials))
            .setApplicationName(APPLICATION_NAME)
            .build()

【讨论】:

  • 我很困惑。我使用的 IDE 支持 Maven,但不支持 Gradle,而且我不知道如何将我在谷歌表格上的服务帐户与我的代码连接起来。你能帮帮我吗?
【解决方案2】:

我使用以下代码解决了这个问题:

credential = new GoogleCredential.Builder()
            .setTransport(transport)
            .setJsonFactory(jsonFactory)
            .setServiceAccountId("618982716759-9ele96d95b7gqar6tn2ofa7jrjrudlol@developer.gserviceaccount.com")
            .setServiceAccountPrivateKeyFromP12File(p12)
            .setServiceAccountScopes(SCOPES_ARRAY).build();
            credential.refreshToken();

所以没有 .setServiceAccountUser("me@gmail.com")。现在它可以正常工作了。

【讨论】:

  • 当我尝试访问工作表时,它要求我请求访问。如何为用户提供权限?
  • @BiscuitCoder 尝试将工作表(来自 google 工作表 UI)共享到服务帐户电子邮件地址。
【解决方案3】:

使用 Google 服务帐户访问 Google 表格的另一种方法是:

InputStream inputStream = getAssets().open("service_account_key.json"); // put your service account's key.json file in asset folder.

GoogleCredential googleCredential = GoogleCredential.fromStream(inputStream)
                  .createScoped(Collections.singleton(SheetsScopes.SPREADSHEETS_READONLY));

这里需要注意的重点:

  • 您应该编写代码块以在线程或异步调用中访问 Google 表格。
  • 如果您在 android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork 处遇到任何异常,例如 android.os.NetworkOnMainThreadException,请在进行异步调用之前包含这些行:

    StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder() .permitAll().build(); StrictMode.setThreadPolicy(policy);

详细解释请参考以下文档:

https://developers.google.com/sheets/quickstart/android?hl=es-419#step_5_setup_the_sample https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests

Android 快乐。

【讨论】:

  • getAssets() 中的代码是什么?没有它,有点难以理解。
  • 在 Android 中,如果你在 Fragment 中,我猜它只是“context.getAssets()”,或者在 Activity 中只是“getAssets()”