【问题标题】:Gmail Java APIs to use service account with OAuth 2.0Gmail Java API 使用 OAuth 2.0 服务帐户
【发布时间】:2021-06-22 00:24:38
【问题描述】:

我是 Google API 的新手。我有一个使用自己的域注册的 google 工作区帐户。 我希望能够使用 google java api 对用户邮箱执行操作。 我正在使用quick start 项目并将身份验证用作范围。

我可以对我执行 OAuth 同意的用户帐户执行操作。

看例子

 String user = "me";
            ListLabelsResponse listResponse = service.users()
           .labels().list(user).execute();

它使用“我”关键字作为用户,我想使用帐户中的一个用户并执行相同的操作。 当我将“我”切换到我域中的一个用户时,我得到以下信息。

GET https://gmail.googleapis.com/gmail/v1/users/user@somedomain.what.ever/labels
{
  "code" : 403,
  "errors" : [ {
    "domain" : "global",
    "message" : "Delegation denied for admin@mirncast.uk",
    "reason" : "forbidden"
  } ],
  "message" : "Delegation denied for admin@mirncast.uk",
  "status" : "PERMISSION_DENIED"
}

据我了解,我需要提供user delegation。 用户委托需要服务帐户。 所以我试着关注这个tutorial

我的代码最终变成了

Set<String> scope = Collections.singleton(GmailScopes.MAIL_GOOGLE_COM);

GoogleCredential credentialFromJson = GoogleCredential
        .fromStream(new FileInputStream(
                "/certificate.json"))
       .createScoped(scope);

GoogleCredential credential = new GoogleCredential.Builder()
        .setTransport(httpTransport)
        .setJsonFactory(jsonFactory)
        .setServiceAccountId(credentialFromJson.getServiceAccountId())
        .setServiceAccountPrivateKey(credentialFromJson.getServiceAccountPrivateKey())
        .setServiceAccountScopes(Collections.singleton(GmailScopes.MAIL_GOOGLE_COM))
        .setServiceAccountUser("user@somedomain.what.ever")
        //.setServiceAccountUser("service@custom-octagon-1234567.iam.gserviceaccount.com")
        .build();


Gmail service = new Gmail.Builder(httpTransport, jsonFactory, credential).setApplicationName("remediation service").build();

String user = "user@somedomain.what.ever";
final var messagesResponse = service.users().messages().list(user).execute();
System.out.println(messagesResponse.getMessages().stream().map(message -> message.getId()).collect(Collectors.joining(",")));

这段代码有一些问题。

只有当我使用我尝试访问的相同电子邮件地址设置 setServiceAccountUser 时,我才能使其工作。从我的角度来看,以这种方式使用 API 会破坏目的。 使用服务帐户时出现以下错误

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
GET https://gmail.googleapis.com/gmail/v1/users/hraman@gsuite-dev.mirncast.uk/messages
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Precondition check failed.",
    "reason" : "failedPrecondition"
  } ],
  "message" : "Precondition check failed.",
  "status" : "FAILED_PRECONDITION"
}

该示例使用了已弃用的 API GoogleCredential,我无法确定基于 OAuth2 身份验证的最新 API 是什么

我的 Maven 依赖项

<dependencies>
    <dependency>
        <groupId>com.google.apis</groupId>
        <artifactId>google-api-services-gmail</artifactId>
        <version>v1-rev20210301-1.31.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.oauth-client</groupId>
        <artifactId>google-oauth-client-jetty</artifactId>
        <version>1.31.4</version>
    </dependency>
</dependencies>

【问题讨论】:

  • 所以当您委托给“user@somedomain.what.ever”时它可以工作?
  • 是的,但这是一个没有服务帐户的实际用户,我希望仅在服务级别 service.users().messages().list("user@somedomain.what .ever")
  • 只要您添加了 .setServiceAccountUser("user@somedomain.what.ever") 我希望它可以像使用我一样工作。但是不知道你为什么不直接使用我。

标签: gmail-api google-auth-library


【解决方案1】:

您首先需要了解 gmail 数据是私人用户数据,只有该数据的所有者才能访问该数据,或者已获得用户授权的应用程序才能访问该数据。在gsuite域账户的情况下,域管理员也可以授予其他人冒充原始用户的权限,但冒充的仍然是访问数据的用户。它就像它的原始用户一样行事。 (理解这个概念很快就会很重要)

您需要了解服务帐户只是一个虚拟用户。它有自己的 google drive 帐户和 google 日历帐户,它还可以像拥有 gmail 帐户一样发送电子邮件。所以和你一样,它只能默认访问自己的私有数据。

现在使用 gsuite,我们可以委派用户权限,允许他们模拟另一个用户“user@somedomain.what.ever”

因此可以设置为允许"service@custom-octagon-1234567.iam.gserviceaccount.com" 冒充"user@somedomain.what.ever" 并代表他们执行操作。只有在代码中才能使用此功能,您必须说明您当前正在模拟的用户,在这种情况下我们使用setServiceAccountUser,如果您不这样做,那么您只是"service@custom-octagon-1234567.iam.gserviceaccount.com",并且只能访问属于"service@custom-octagon-1234567.iam.gserviceaccount.com"的数据

至于使用messages.list 访问数据,以下命令可以互换。

service.users().messages().list("user@somedomain.what.ever")
service.users().messages().list("me")

如果第一个选项不是当前经过身份验证的用户的电子邮件地址或模拟用户,它将失败。通过模拟用户,您实际上就是该用户,因此 API 将服务帐户视为 "user@somedomain.what.ever" 而没有指定模拟,您只是普通的旧服务帐户,只能访问他们的数据。

需要有一种方法来指定来自服务帐户的所有呼叫现在都代表该用户,这就是为什么您需要将setServiceAccountUser 设置为您希望模拟的用户。

【讨论】:

  • 我可以看到你关于 setServiceAccountUser 和使用我的观点。但在这种情况下,我仍然对 OAuth 2.0 感到困惑。看起来我需要为每个请求获取令牌,因为在我的情况下,我将为我域中的几个用户重复相同的操作。因此,一旦这令人困惑,auth 2.0 同意步骤仍然会发生,因为我们在这里讨论的是服务器到服务器模式。我希望我的客户定义并接受一次服务帐户,然后应该自动使用它
  • 您会看到服务帐户并不是真正的 Oauth2。标准 Oauth2 是 3LO,服务帐户是 2LO。它在重复哪一步?是的,您必须连接到每个用户,但如果您正在运行 3LO 并使用刷新令牌,则需要这样做。
  • 让我看看3LO
猜你喜欢
  • 2018-02-19
  • 2015-03-04
  • 2014-12-13
  • 2014-09-13
  • 2014-08-17
  • 1970-01-01
  • 2022-01-15
  • 2015-04-03
  • 1970-01-01
相关资源
最近更新 更多