如何使用Java访问Google日历REST API v3

14

我正在开发一个项目,需要使用Java通过REST访问一组Google日历。

该程序位于私人非Google服务器上,并定期(通过cron job)连接到Google帐户,获取链接到帐户的所有日历列表,获取每个日历上一个月的事件,并返回包含所有信息的XML文件。该程序应能够在没有任何用户输入的情况下执行和访问日历。目前,该项目规定仅读取日历,不进行修改(因此仅进行GET / LIST调用)。

我已查看了Google文档并研究了使用客户端库的示例代码,几乎所有给定的示例都要求在访问日历API之前进行OAuth 2.0用户同意。即使是REST API文档页面也要求您激活OAuth 2.0才能返回请求的信息(否则返回HTTP 40X错误代码和包含错误状态和消息的JSON文件)。

我该如何通过REST调用连接到Google Calendar REST API以获取所需的信息,而不需要在执行时要求用户同意?

还是我把事情复杂化了,只需要“Server Key”,可以在Google Cloud控制台的注册应用程序中找到?
或者我需要同时使用OAuth和开发人员密钥? (我在这里找到了有人在标题为“Google Calendar API v3 hardcoded credentials”的问题下提到过;但是,问题和解决方案是针对PHP的,我不知道在Java中是否需要或可能有类似的解决方案)。

  • 我确实看到了使用服务帐户创建JWT的可能性(使用Google Cloud控制台,在“注册应用程序”中注册为Web应用程序,它位于“证书”下),但我没有找到如何将其与Java程序的REST调用结合使用。
  • 以下链接(http://aleksz-programming.blogspot.be/2012/11/google-calendar-api.html)提到可以通过“Web服务器应用程序”或“已安装的应用程序”访问信息,但未详细说明。然而,在谷歌OAuth 2.0文档页面上,“Web服务器应用程序”部分仍需要用户输入和同意。哪个是真的?
    我还发现了这个页面(https://developers.google.com/accounts/docs/OAuth2Login),但没有看到如何在不使用用户同意页面的情况下使用它。
    我看到一些关于“两条腿的OAuth”的参考资料。但似乎这不是OAuth 2.0,而是一种版本 1 的做法。这对我的项目是可能的解决方案吗?如果是,我需要从Google Cloud控制台获取哪些信息才能使其运作?

让我明确一下,你想让你的应用程序访问某人的日历,即使他们没有授予你权限? - pinoyyid
关闭。我希望能够通过程序访问自己的日历,而无需每次程序运行时手动授予权限(通过Google授权页面)。 - AntonH
2个回答

14

如果您只需要访问特定一组日历,我建议您创建一个服务帐号并与该帐号共享必要的日历。

操作步骤如下:

  1. 在此 Cloud 控制台中创建一个“服务帐号”(它位于“Web 应用程序”/"证书"下)。
  2. 下载私钥并将其存储在安全的地方。
  3. 注意与服务帐号相关联的电子邮件地址。
  4. 使用日历用户界面将任何必要的日历与此电子邮件地址共享。
  5. 安装 Google API Java Client libraries (https://developers.google.com/api-client-library/java/apis/)。

然后您就可以使用以下代码:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.json.gson.GsonFactory;
import java.io.File;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import java.util.Arrays;
import com.google.api.services.calendar.Calendar;

GoogleCredential credentials = new GoogleCredential.Builder().setTransport(GoogleNetHttpTransport.newTrustedTransport())
  .setJsonFactory(new GsonFactory())
  .setServiceAccountId("<service account email address>@developer.gserviceaccount.com")
  .setServiceAccountScopes(Arrays.asList("https://www.googleapis.com/auth/calendar.readonly"))
  .setServiceAccountPrivateKeyFromP12File(new File("<private key for service account in P12 format>-privatekey.p12"))
.build();
Calendar client = new Calendar.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), credentials).build();
client.<do calendar stuff>.execute();

如果您是一个域管理员,需要访问属于您域的所有Google应用账户的日历而无需征得各个用户的同意,则代替上述第4步骤:

  1. 记下与服务账户相关联的客户端ID。在client_secrets.json文件中可以找到它,通常形式为1234.apps.googleusercontent.com。
  2. 授权该客户端代表组织中的用户进行请求。请参见https://support.google.com/a/answer/162106?hl=zh-Hans上的步骤 - 使用您稍后将请求的任何范围。

现在您可以编写以下代码:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.json.gson.GsonFactory;
import java.io.File;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import java.util.Arrays;
import com.google.api.services.calendar.Calendar;

GoogleCredential credentials = new GoogleCredential.Builder().setTransport(GoogleNetHttpTransport.newTrustedTransport())
  .setJsonFactory(new GsonFactory())
  .setServiceAccountId("<service account email address>@developer.gserviceaccount.com")
  .setServiceAccountScopes(Arrays.asList("https://www.googleapis.com/auth/calendar"))
  .setServiceAccountPrivateKeyFromP12File(new File("<private key for service account in P12 format>-privatekey.p12"))
  .setServiceAccountUser("<domain user whose data you need>@yourdomain.com")
.build();
Calendar client = new Calendar.Builder(GoogleNetHttpTransport.newTrustedTransport(), new GsonFactory(), credentials).build();
client.<do calendar stuff as that user>()

非常感谢您的回复。我已经找到了这些信息,但是直到在这里阅读它时才清楚地理解了它。非常感谢。只是一个小提示:Calendar client = Calendar.Builder(...) 的代码应该是 *Calendar client = new Calendar.Builder(...)*。 - AntonH
非常感谢。我已经测试了代码,虽然它确实返回了一个日历,但似乎我无法使用它访问该日历的事件。这是因为我没有设置应用程序名称,还是我漏掉了什么? - AntonH
我认为设置应用程序名称没有任何区别。你能发布一下你看到的错误吗? - aeijdenberg
当我不使用 setApplicationName() 时,只会收到一个警告,提示没有设置应用程序名称。并且没有显示任何错误,但是当我使用 events = client.getEvents().list("primary").execute(); 然后使用 events.getItems() 时,它返回一个空列表,即使我确定日历中有事件。 - AntonH
好的,现在它可以工作了。是我的错,我没有注意到第四步,我需要与证书的电子邮件地址共享我的日历。现在我完成了这一步,我可以获取事件列表("email_address@...")并浏览它们。非常感谢。 - AntonH
1
这个答案适用于 API v3 吗? - Tomasz Waszczyk

2

除了@aeijdenberg的回答中提到的步骤之外,服务帐户现在需要通过将其添加到其CalendarList中来显式接受共享日历。请参见:

服务帐户不再自动接受共享日历[148804709] https://issuetracker.google.com/issues/148804709


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接