Подтвердить что ты не робот

"Login Required" 401 Несанкционированное сообщение при вызове API-интерфейса Google Calendar v3 с помощью учетной записи службы через OAuth 2.0

Во-первых, позвольте мне объяснить, что я пытаюсь сделать, поскольку это вопрос из двух частей.

Я создаю службу JAX-RS, которая внутренне аутентифицируется с помощью учетной записи Google через OAuth2, поэтому она может получать доступ и управлять календарями Google. Эта услуга будет вызываться веб-сайтом (Joomla), который позволит пользователям, которые заходят на сайт, регистрировать свой адрес электронной почты с событиями в календаре, чтобы они могли получать уведомления по электронной почте, когда события близки. Эти пользователи не знают основную учетную запись Google.

Итак, мой первый вопрос, правильно ли используется проверка подлинности учетной записи? Глядя на документы Google, это единственный прецедент, который подходит для проверки подлинности, отличной от человека (например, для проверки подлинности на сервере).

Вторая проблема заключается в том, что я написал какой-то код для этого, но я возвращаю ответ 401/Unauthorized обратно обратно следующую ошибку, когда я вызываю метод execute в канале календаря. Этот код был снят с образцов Google.

Я использую v3-rev3-1.5.0-бета API календаря Google. Я включил API-интерфейс календаря в учетной записи и создал и загрузил закрытый ключ, который используется в приведенном ниже коде.

Я вызываю код ниже в Eclipse локально, а не с веб-сервера.

Итак, первый вызов, который я делаю, - это вернуть объект учетных данных (Id обфускации с помощью X):

private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
.
.
.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
            .setJsonFactory(JSON_FACTORY)
            .setServiceAccountId("[email protected]")
            .setServiceAccountScopes(CalendarScopes.CALENDAR)
            .setServiceAccountPrivateKeyFromP12File(new File("D:/3cd8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXd635e-privatekey.p12"))
            .build();

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

 Calendar calendar = Calendar.builder(HTTP_TRANSPORT, JSON_FACTORY)
        .setApplicationName("TestApp-Calendar/1.0").setHttpRequestInitializer(credential)
        .build();

 CalendarList feed = calendar.calendarList().list().execute();

Вызов функции list(). execute() приводит к следующей ошибке:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Несанкционированный {    "код": 401,    "ошибки": [{      "домен": "глобальный",      "местоположение": "Авторизация",      "locationType": "header",      "message": "Login Required",      "причина": "требуется",   }],    "message": "Login Required" }     на com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:159)     на com.google.api.client.googleapis.json.GoogleJsonResponseException.execute(GoogleJsonResponseException.java:187)     at com.google.api.client.googleapis.services.GoogleClient.executeUnparsed(GoogleClient.java:115)     at com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.java:112)     at com.google.api.services.calendar.Calendar $CalendarList $List.execute(Calendar.java:510)     на uk.org.reigatepriorybowmen.Service.registerEmailForAllCalendarEvents(Service.java:55)     на uk.org.reigatepriorybowmen.Service.main(Service.java:74)

Учетная запись службы настроена следующим образом:

enter image description here

Итак, что я делаю неправильно?! Я потратил много времени на поиск решений, поэтому это моя последняя надежда.

Большое спасибо за вашу помощь.

Джастин

4b9b3361

Ответ 1

Что касается 401, то это выглядит как-то странно с Google API. Я нахожу первый раз, когда я запускаю свое приложение через некоторое время, а затем, по-видимому, случайные интервалы часов разбросаны, я всегда получаю 401. На втором или третьем раунде он фактически входит в систему.

Поведение даже учитывается в примере кода API Google Task здесь.

Обратите внимание на этот код:

void onRequestCompleted() {
received401 = false;
}

void handleGoogleException(IOException e) {
if (e instanceof GoogleJsonResponseException) {
  GoogleJsonResponseException exception = (GoogleJsonResponseException) e;
  if (exception.getStatusCode() == 401 && !received401) {
    received401 = true;
    accountManager.invalidateAuthToken(credential.getAccessToken());
    credential.setAccessToken(null);
    SharedPreferences.Editor editor2 = settings.edit();
    editor2.remove(PREF_AUTH_TOKEN);
    editor2.commit();
    gotAccount();
    return;
  }
}
Log.e(TAG, e.getMessage(), e);

}

В собственном примере кода Google они предусмотрели первый раз 401. Это может быть специальная проблема безопасности, которая должна обрабатываться в коде.

Ответ 2

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

То, как вы используете ServiceAccount прямо сейчас, действителен только для доступа к данным, связанным с вашим приложением или к вашей учетной записи службы. Поскольку вы хотите получить доступ к данным Календаря Google, вы должны быть администратором домена Google Apps с привилегиями доступа к любой из ваших пользовательских данных вашего домена. Сообщите мне, если я не прав. Данные Календаря Google принадлежат пользователям, поэтому вам нужно указать, какого пользователя вы пытаетесь выдавать себя за него.

Чтобы сделать это с помощью Java, вам кажется, что вам нужно использовать GoogleCredential.Builder # setServiceAccountUser (String)

Попробуйте:

GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
        .setJsonFactory(JSON_FACTORY)
        .setServiceAccountId("[email protected]")
        .setServiceAccountScopes(CalendarScopes.CALENDAR)
        .setServiceAccountPrivateKeyFromP12File(new File("/path/to/privatekey.p12"))
        .setServiceAccountUser("[email protected]")
        .build();

Кроме того, как только вы создали ключ учетной записи службы в своем проекте API (из консоли API), вам придется добавить этот проект в список уполномоченного стороннего приложения в cPanel. Более подробную информацию об этом можно найти здесь. "Идентификатор клиента", который вам нужно использовать, - это тот, который связан с ключом учетной записи службы и выглядит как <APP_ID>-<OTHER_KEY>.apps.googleusercontent.com

Ответ 3

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

example in google analytics

Ответ 4

Я разработчик Android Mobile. Я использую приложение Google Calendar API (OAuth 2.0). Я также столкнулся с такой же ошибкой. Но теперь я решил ошибку. Я пишу сценарий и решение для него.
URL-адрес получения события календаря для конкретного календаря (Mobile App API):

https://www.googleapis.com/calendar/v3/calendars/en.indian%23holiday%40group.v.calendar.google.com/events?access_token=ya29.AHES6ZQyP3iDZM8PF-7ZqZE8oKmWAgUX55sPGPTjGbM5A

В этом URL-адресе мы должны добавить calendarId. Формат URL-адреса:

https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events

Я не кодировал CalendarId перед добавлением в url. Я закодировал calendarId, а затем сделал запрос Http. Все прошло хорошо.

final String accessToken=dataStore.getAccessToken();

    List<NameValuePair> params = new LinkedList<NameValuePair>();
    params.add(new BasicNameValuePair("access_token", accessToken));
    String paramString = URLEncodedUtils.format(params, "utf-8");

    final String url = String.format(AuthConstants.CALENDAR_EVENTS_URI,URLEncoder.encode(calendarId))+"?"+ paramString;

Теперь события календаря возвращаются в JSON.

Ответ 5

ОК, поэтому я играл с этим и пропустил ошибку 401, но теперь я нахожусь на 403. Однако я подробно расскажу о написанном мной коде, который, по крайней мере, отвечает на проблему 401.

Я не изменил ни одну из настроек учетной записи службы или не сгенерировал ни один из ключей и т.д., это все равно.

Что изменилось, это часть кода, и я обновился до версии v3r20lv1.12.0-beta API календаря.

Код ниже, как описано выше в OP, остается неизменным, то есть:

private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
.
.
.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
        .setJsonFactory(JSON_FACTORY)
        .setServiceAccountId("[email protected]")
        .setServiceAccountScopes(CalendarScopes.CALENDAR)
        .setServiceAccountPrivateKeyFromP12File(new File("D:/3cd8XXXXXXXXXXXXXXXXXXXXXXXXXXXXXd635e-privatekey.p12"))
        .build();

Код для получения дескриптора для Календаря изменился, но это было вызвано изменением API:

Calendar calendar = new Calendar(HTTP_TRANSPORT, JSON_FACTORY, credential);

На этом этапе я могу выполнить итерацию по календарю и увидеть события, выполнив следующий код (это печатает сводку событий):

Events events = calendar.events().list(CALENDAR_ID).execute();

for (Event event : events.getItems())
{
    System.out.println(event.getSummary());
}

Но, если я попытаюсь обновить событие, я получаю 403 Forbidden ошибку. Я создам для этого отдельный поток и свяжу эти два потока вместе.

Спасибо всем за вашу помощь!

EDIT: здесь - это новый поток.

Ответ 6

Вы запрашивали разрешение для соответствующих областей? Вы всегда должны включать https://www.googleapis.com/auth/userinfo.profile