Использование Java и OAuth 2.0 для подключения к контактам Google

У меня проблемы с подключением к API контактов Google с помощью Java с использованием OAuth 2.0. После тщательного поиска я достиг точки, где, как мне кажется, я могу подключиться, но в конечном итоге получаю сообщения об ошибках.

Я сделал следующее:

1) Создала учетную запись службы OAuth 2.0 через консоль разработчика Google для проекта.

2) Следуя инструкциям по "делегированию полномочий домена на учетную запись службы", которую я нашел по адресу https://developers.google.com/identity/protocols/OAuth2ServiceAccount, я авторизовал идентификатор клиента учетной записи службы, созданной на шаге #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: невозможно запросить контакты, принадлежащие другому пользователю. Я считаю, что мне нужно сохранить setServiceAccountUser(...), но я не могу обойти исключение ответа токена.

Я не уверен, куда идти отсюда.

Спасибо за любой совет или помощь, которую вы можете предоставить.

2 ответа

Решение

Для тех, кто заинтересован или сталкивается с той же проблемой, что и я, я наткнулся на эту ссылку, которая решает проблему. Главной проблемой было "/", которое у меня было в конце объема, который мне нужно было использовать. Его нужно было добавить в мой код, чтобы он соответствовал объему, который я дал для доступа к 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());
    }
}

Похоже, вы используете поток кода авторизации из OAuth2 (читайте больше в этом хорошем посте).

В этом случае после уже выполненного шага (зарегистрируйте свое приложение в консоли Google + зарегистрируйте свое приложение для доступа к API контактов), вашему приложению потребуется:

  1. Предоставьте пользователю URL-адрес авторизации: пользователь щелкнет и предоставит вашему приложению разрешение на чтение нужных вам данных.
  2. Получив разрешение пользователя, вы получите authorization code,
  3. Используя это authorization code, вы обменяете его token,
  4. Теперь с token в ваших руках, вызовите API контактов Google.

Пользовательские библиотеки Java

Я советую использовать популярные библиотеки Java, которые обрабатывают весь этот поток для вас.

Две популярные библиотеки:

  • Библиотеки Google OAuth2 API для Java: ориентированы на API Google OAuth2;

  • Apache Oitu: реализация протокола OAuth/OAuth2 в Java. Он также охватывает другие реализации, относящиеся к семейству OAuth, такие как JWT, JWS и OpenID Connect.

Оба имеют поддержку Maven.

Другим возможным решением является использование служб с широкими адресными книгами, таких как CloudSponge.com, которые предлагают Java API и поддержку других популярных источников, если вы планируете не только Gmail.

Другие вопросы по тегам