使用服务帐户创建 Google 日历活动时出错

时间:2020-12-19 10:37:43

标签: java google-api google-calendar-api google-api-java-client service-accounts

我需要在日历上创建 Google 日历活动,并将其他用户添加为该活动的参与者。目的是在未经用户同意的情况下向应用用户发送日历事件 (O-Auth)。

阅读谷歌的文档后,我发现我需要一个服务帐户。因此,我从我们 G-Suite 的电子邮件地址之一 noreply@xxxxx.com 创建了一个项目和一个服务帐户,并为此启用了日历 API。

我创建并下载了一个密钥对 (JSON),其内容是,

{
  "type": "service_account",
  "project_id": "*****",
  "private_key_id": "bdbbcd**************49f77d599f2",
  "private_key": "**"
  "client_email": "******@*****.iam.gserviceaccount.com",
  "client_id": "11083******576856",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/****dev%40*****kdev.iam.gserviceaccount.com"
}

根据文档,我继续编写身份验证流程代码,

public static GoogleCredential doOauth( String credsPath ) throws IOException
    {
        GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream(credsPath))
                .createScoped(Collections.singleton(CalendarScopes.CALENDAR));
        System.out.println(credential);
        return credential;
    }

credential 对象包含密钥文件中的大部分详细信息。但是,字段 serviceAccountUseraccessTokenrefreshTokenclientAuthenticationrequestInitializer 具有 null 值。 (我猜这里有问题)

现在,使用 credentialObject,我继续按照文档编写代码以创建事件。

GoogleCredential credential = doOauth(CREDENTIALS_FILE_PATH);
        Event event = new Event().setSummary("Google I/O 2015").setLocation("800 Howard St., San Francisco, CA 94103")
                .setDescription("A chance to hear more about Google's developer products.");
        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        DateTime startDateTime = new DateTime("2020-12-28T09:00:00-07:00");
        EventDateTime start = new EventDateTime().setDateTime(startDateTime).setTimeZone("America/Los_Angeles");
        event.setStart(start);

        DateTime endDateTime = new DateTime("2020-12-28T17:00:00-07:00");
        EventDateTime end = new EventDateTime().setDateTime(endDateTime).setTimeZone("America/Los_Angeles");
        event.setEnd(end);

        String[] recurrence = new String[] { "RRULE:FREQ=DAILY;COUNT=2" };
        event.setRecurrence(Arrays.asList(recurrence));

        EventAttendee[] attendees = new EventAttendee[] { new EventAttendee().setEmail("myemailaddress@gmail.com.com") };
        event.setAttendees(Arrays.asList(attendees));

        EventReminder[] reminderOverrides = new EventReminder[] {
                new EventReminder().setMethod("email").setMinutes(24 * 60),
                new EventReminder().setMethod("popup").setMinutes(10), };
        Event.Reminders reminders = new Event.Reminders().setUseDefault(false)
                .setOverrides(Arrays.asList(reminderOverrides));
        event.setReminders(reminders);
        String calendarId = "primary";
        Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
                .setApplicationName("testapp").build();
        event = service.events().insert(calendarId, event).execute();
        System.out.printf("Event created: %s\n", event.getHtmlLink());

但是,这导致了错误,

{
  "code" : 403,
  "errors" : [ {
    "domain" : "calendar",
    "message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority.",
    "reason" : "forbiddenForServiceAccounts"
  } ],
  "message" : "Service accounts cannot invite attendees without Domain-Wide Delegation of Authority."
}

Domain-Wide Delegation 上花费时间后,我明白如果我们必须以其他用户身份从我们的 g 套件发送事件,这是需要的,这对于我的问题来说是不需要的。但是,为了调试,我继续提供 Domain-Wide Delegation 并重新运行程序。同样的错误又来了。

因此,我从 event 对象中删除了受邀者/与会者并重新运行了应用程序。这次程序运行没有任何错误,但生成的事件链接在点击时显示Could not find the requested event

我在 google 开发者链接上没有看到任何通过 Java 客户端库使用服务帐户的示例。

你能否让我知道这里出了什么问题,以及关于如何从我的项目中创建谷歌日历事件的官方/工作文档添加其他(非 g 套件)用户作为参与者添加,以便我无需征得其他用户的同意即可将活动添加到他们自己的日历中?

谢谢。

1 个答案:

答案 0 :(得分:0)

首先我想说这是一个新的东西,如果你看到很多问题,并且教程没有说明你需要这样做,因为服务帐户曾经能够发送邀请,这是一些东西谷歌大约一年前关闭。

这应该可以工作,但我没有测试过,因为我无法再访问 Gsuite 帐户。您需要让 gsuite 管理员将 domain wide delegation 设置为另一个用户。然后服务帐户需要模拟该用户,以便显示该用户是发送邀请的用户。

关于如何添加它的唯一示例是在 .net 中

.net 示例

a1 = popular_trio['actor_1_facebook_likes']
a2 = popular_trio['actor_2_facebook_likes']
a3 = popular_trio['actor_3_facebook_likes']

c1 = (a1/2) < a2
c2 = (a2/2) < a1
c3 = (a3/2) < a2
c4 = (a1/2) < a3
c5 = (a2/2) < a3
c6 = (a3/2) < a1

popular_trio[~(c1|c2|c3|c4|c5|c6)] # or try popular_trio[~((c1)|(c2)|(c3)|(c4)|(c5)|(c6))]

Java 示例猜测

我不是 Java 开发人员,但 .net 客户端库和 Java 客户端库的开发方式非常接近。我猜你正在寻找一个叫做 setServiceAccountUser

的方法
var gsuiteUser = "se@clawskeyboard.com";
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
            {
                User = gsuiteUser,
                Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }

            }.FromCertificate(certificate);