1

我在使用 OAuth 2.0 通过 Java 连接到 Google Contacts API 时遇到问题。经过广泛的搜索,我已经达到了可以连接的地步,但在最终过程中收到错误消息。

我做了以下事情:

1) 通过 Google 的开发者控制台为该项目创建了一个 OAuth 2.0 服务帐户。

2) 按照我在https://developers.google.com/identity/protocols/OAuth2ServiceAccount找到的“将域范围的权限委派给服务帐户”的步骤,我已授权在步骤 # 中创建的服务帐户的客户端 ID 1 到范围https://www.google.com/m8/feeds/的联系人(读/写)。

3)这是我基于其他成员帖子的代码:

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.gdata.client.contacts.ContactsService;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;

public class Connector {
private static ContactsService contactService = null;
private static HttpTransport httpTransport;

private static final String APPLICATION_NAME = "MyProject";
private static final String SERVICE_ACCOUNT_EMAIL = "service-account-email-address@developer.gserviceaccount.com";
private static final java.util.List<String> SCOPE = Arrays.asList("https://www.google.com/m8/feeds");

private Connector() {
    // explicit private no-args constructor
}

public static ContactsService getInstance() {
    if (contactService == null) {
        try {
            contactService = connect();
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return contactService;
}

private static ContactsService connect() throws GeneralSecurityException, IOException {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();

    java.io.File p12File = new java.io.File("MyProject.p12");

    // @formatter:off
    GoogleCredential credential = new GoogleCredential.Builder()
                                                    .setTransport(httpTransport)
                                                    .setJsonFactory(JacksonFactory.getDefaultInstance())
                                                    .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
                                                    .setServiceAccountScopes(SCOPE)
                                                    .setServiceAccountPrivateKeyFromP12File(p12File)
                                                    .setServiceAccountUser("user@example.com")
                                                    .build();
    // @formatter:on

    if (!credential.refreshToken()) {
        throw new RuntimeException("Failed OAuth to refresh the token");
    }

    ContactsService myService = new ContactsService(APPLICATION_NAME);
    myService.setOAuth2Credentials(credential);

    return myService;
}
}

从理论上讲,我相信这应该是可行的。但是,当credential.refreshToken()执行时,我收到错误:

com.google.api.client.auth.oauth2.TokenResponseException: 403 Forbidden
{
  "error" : "access_denied",
  "error_description" : "Requested client not authorized."
}

如果我删除setServiceAccountUser("user@example.com"),我会收到错误消息com.google.gdata.util.ServiceForbiddenException: Cannot request Contacts belongs to another user。我相信我需要保留 setServiceAccountUser(...) 但无法通过令牌响应异常。

我不确定从这里去哪里。

感谢您提供的任何建议或帮助。

4

2 回答 2

4

对于任何有兴趣或遇到与我相同的问题的人,我遇到了解决问题的这个链接。主要问题是我需要使用的范围末尾的“/”。它需要添加到我的代码中,以匹配我提供给服务帐户 API 访问权限的范围。

我的最终代码:

HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();

String APPLICATION_NAME = "MyProject";
String SERVICE_ACCOUNT_EMAIL = "my-service-account@developer.gserviceaccount.com";
java.io.File p12File = new java.io.File("MyProject.p12");

GoogleCredential credential = new GoogleCredential.Builder()                                                                    
      .setTransport(httpTransport)                                                     
      .setJsonFactory(jsonFactory)                                                   
      .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)   
      .setServiceAccountScopes(
            Collections.singleton("https://www.google.com/m8/feeds/"))                                                          
      .setServiceAccountPrivateKeyFromP12File(p12File)                                                  
      .setServiceAccountUser("user@example.com")
      .build();

if (!credential.refreshToken()) {
    throw new RuntimeException("Failed OAuth to refresh the token");
}

ContactsService service = new ContactsService(APPLICATION_NAME);
service.setOAuth2Credentials(credential);

Query gQuery = new Query(new java.net.URL("https://www.google.com/m8/feeds/groups/user@example.com/full"));
gQuery.setMaxResults(32767);
ContactGroupFeed groupFeed = service.query(gQuery, ContactGroupFeed.class);

for (ContactGroupEntry group : groupFeed.getEntries()) {
    System.out.println("group: " + group.getTitle().getPlainText());

    Query cQuery = new Query(new java.net.URL("https://www.google.com/m8/feeds/contacts/user@example.com/full"));
    cQuery.setMaxResults(32767);
    String grpId = group.getId();
    cQuery.setStringCustomParameter("group", grpId);
    ContactFeed feed = service.query(cQuery, ContactFeed.class);

    for (ContactEntry contact : feed.getEntries()) {
        System.out.println("name: " + contact.getTitle().getPlainText());
    }
}
于 2015-06-01T02:05:54.980 回答
1

看来您正在使用来自 OAuth2的授权代码流(在这篇好文章中阅读更多内容)。

在这种情况下,在您已经执行的步骤之后(在 Google 控制台中注册您的应用 + 注册您的应用以访问联系人 API),您的应用将需要:

  1. 向用户提供授权 URL:用户将单击并授予您的应用读取所需数据的权限。
  2. 授予用户权限后,您将收到一个authorization code.
  3. 使用它authorization code,您将用token.
  4. 现在,token在您的手中,调用 Google Contacts API。

用户 Java 库

我的建议是使用流行的 Java 库来为您处理所有这些流程。

两个流行的库是:

两者都有 Maven 支持。

另一种可能的解决方案是使用CloudSponge.com等广泛的地址簿服务,它提供Java API并支持其他流行来源,以防您计划的不仅仅是 Gmail。

于 2015-05-28T22:20:28.717 回答