Обзор
Назначение: В этом документе объясняется, как использовать вспомогательный класс GoogleCredential для авторизации по протоколу OAuth 2.0 с сервисами Google. Информацию об общих функциях OAuth 2.0, которые мы предоставляем, см. в разделе «OAuth 2.0 и клиентская библиотека Google OAuth для Java» .
Краткое описание: Для доступа к защищенным данным, хранящимся в сервисах Google, используйте авторизацию OAuth 2.0 . API Google поддерживают потоки OAuth 2.0 для различных типов клиентских приложений. Во всех этих потоках клиентское приложение запрашивает токен доступа, который связан только с вашим клиентским приложением и владельцем защищенных данных, к которым осуществляется доступ. Токен доступа также связан с ограниченной областью действия, определяющей тип данных, к которым имеет доступ ваше клиентское приложение (например, «Управление задачами»). Важной целью OAuth 2.0 является обеспечение безопасного и удобного доступа к защищенным данным, минимизируя при этом потенциальные последствия в случае кражи токена доступа.
Пакеты OAuth 2.0 в библиотеке Google API Client Library for Java созданы на основе универсальной библиотеки Google OAuth 2.0 Client Library for Java .
Подробную информацию см. в документации Javadoc для следующих пакетов:
- com.google.api.client.googleapis.auth.oauth2 (из google-api-client )
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (из google-api-client-appengine )
Консоль Google API
Прежде чем получить доступ к API Google, необходимо настроить проект в консоли Google API для целей аутентификации и выставления счетов, независимо от того, является ли ваш клиент установленным приложением, мобильным приложением, веб-сервером или клиентом, работающим в браузере.
Инструкции по правильной настройке учетных данных см. в справке консоли API .
Удостоверение личности
GoogleCredential
GoogleCredential — это потокобезопасный вспомогательный класс для OAuth 2.0, предназначенный для доступа к защищенным ресурсам с использованием токена доступа. Например, если у вас уже есть токен доступа, вы можете отправить запрос следующим образом:
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Plus plus = new Plus.builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), credential) .setApplicationName("Google-PlusSample/1.0") .build();
Идентификатор Google App Engine
Этот альтернативный способ получения учетных данных основан на Java API Google App Engine App Identity . В отличие от учетных данных, при которых клиентское приложение запрашивает доступ к данным конечного пользователя, API App Identity предоставляет доступ к собственным данным клиентского приложения.
Используйте AppIdentityCredential (из google-api-client-appengine ). Эти учетные данные намного проще, поскольку Google App Engine берет на себя все детали. Вам нужно только указать необходимую область действия OAuth 2.0.
Пример кода взят из urlshortener-robots-appengine-sample :
static Urlshortener newUrlshortener() { AppIdentityCredential credential = new AppIdentityCredential( Collections.singletonList(UrlshortenerScopes.URLSHORTENER)); return new Urlshortener.Builder(new UrlFetchTransport(), GsonFactory.getDefaultInstance(), credential) .build(); }
хранилище данных
Токен доступа обычно имеет срок действия 1 час, после чего при попытке его использования вы получите ошибку. GoogleCredential автоматически обновляет токен, что просто означает получение нового токена доступа. Это делается с помощью долгосрочного токена обновления, который обычно получается вместе с токеном доступа, если вы используете параметр access_type=offline во время процесса авторизации (см. GoogleAuthorizationCodeFlow.Builder.setAccessType(String) ).
Большинству приложений потребуется сохранять токен доступа и/или токен обновления учетных данных. Для сохранения токенов доступа и/или обновления вы можете предоставить собственную реализацию DataStoreFactory с классом StoredCredential ; или вы можете использовать одну из следующих реализаций, предоставляемых библиотекой:
- AppEngineDataStoreFactory : сохраняет учетные данные, используя API хранилища данных Google App Engine.
- MemoryDataStoreFactory : "сохраняет" учетные данные в памяти, что полезно только в качестве кратковременного хранилища на протяжении всего времени существования процесса.
- FileDataStoreFactory : сохраняет учетные данные в файле.
Пользователям AppEngine: AppEngineCredentialStore устарел и будет вскоре удален. Мы рекомендуем использовать AppEngineDataStoreFactory с StoredCredential . Если у вас учетные данные хранятся в старом формате, вы можете использовать добавленные вспомогательные методы migrateTo(AppEngineDataStoreFactory) или migrateTo(DataStore) для миграции.
Вы можете использовать DataStoreCredentialRefreshListener и установить его для учетных данных с помощью GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) ).
Поток авторизационного кода
Используйте поток авторизационного кода, чтобы позволить конечному пользователю предоставить вашему приложению доступ к своим защищенным данным в API Google. Протокол для этого потока указан в разделе «Предоставление авторизационного кода» .
Данный процесс реализован с использованием GoogleAuthorizationCodeFlow . Шаги следующие:
- Пользователь входит в ваше приложение. Вам необходимо связать этого пользователя с уникальным идентификатором, присвоенным вашему приложению.
- Вызовите AuthorizationCodeFlow.loadCredential(String ) на основе идентификатора пользователя, чтобы проверить, известны ли уже учетные данные конечного пользователя. Если да, то на этом все.
- В противном случае вызовите AuthorizationCodeFlow.newAuthorizationUrl() и перенаправьте браузер конечного пользователя на страницу авторизации, чтобы предоставить вашему приложению доступ к его защищенным данным.
- Затем сервер авторизации Google перенаправит браузер обратно на URL-адрес перенаправления, указанный вашим приложением, вместе с параметром запроса
code. Используйте параметрcodeдля запроса токена доступа с помощью AuthorizationCodeFlow.newTokenRequest(String ). - Используйте AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String ) для сохранения и получения учетных данных для доступа к защищенным ресурсам.
В качестве альтернативы, если вы не используете GoogleAuthorizationCodeFlow , вы можете использовать классы более низкого уровня:
- Используйте DataStore.get(String) для загрузки учетных данных из хранилища на основе идентификатора пользователя.
- Используйте GoogleAuthorizationCodeRequestUrl , чтобы перенаправить браузер на страницу авторизации.
- Используйте AuthorizationCodeResponseUrl для обработки ответа авторизации и анализа кода авторизации.
- Используйте GoogleAuthorizationCodeTokenRequest для запроса токена доступа и, возможно, токена обновления.
- Создайте новый объект GoogleCredential и сохраните его с помощью метода DataStore.set(String, V) .
- Получите доступ к защищенным ресурсам, используя
GoogleCredential. Истекшие токены доступа будут автоматически обновлены с помощью токена обновления (если применимо). Убедитесь, что используете DataStoreCredentialRefreshListener и установите его для учетных данных с помощью GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener ).
При настройке проекта в консоли Google API вы выбираете различные учетные данные в зависимости от используемого потока. Более подробную информацию см. в разделе «Настройка OAuth 2.0 и сценариев OAuth 2.0» . Ниже приведены фрагменты кода для каждого из потоков.
Приложения веб-сервера
Протокол для этого потока описан в статье «Использование OAuth 2.0 для веб-серверных приложений» .
Эта библиотека предоставляет вспомогательные классы сервлетов, значительно упрощающие процесс авторизации для базовых сценариев использования. Вам нужно всего лишь предоставить конкретные подклассы AbstractAuthorizationCodeServlet и AbstractAuthorizationCodeCallbackServlet (из google-oauth-client-servlet ) и добавить их в файл web.xml. Обратите внимание, что вам все еще нужно позаботиться о входе пользователя в ваше веб-приложение и извлечь идентификатор пользователя.
public class CalendarServletSample extends AbstractAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance(), "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet { @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { // handle error } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder( new NetHttpTransport(), GsonFactory.getDefaultInstance() "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]", Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
приложения Google App Engine
Процесс авторизации с помощью кода в App Engine практически идентичен процессу авторизации с помощью кода в сервлетах, за исключением того, что мы можем использовать Java API пользователей Google App Engine. Для включения Java API пользователей пользователь должен быть авторизован; информацию о перенаправлении пользователей на страницу входа в систему, если они еще не авторизованы, см. в разделе «Безопасность и аутентификация» (в web.xml).
Основное отличие от случая с сервлетами заключается в том, что вы предоставляете конкретные подклассы AbstractAppEngineAuthorizationCodeServlet и AbstractAppEngineAuthorizationCodeCallbackServlet (из google-oauth-client-appengine) . Они расширяют абстрактные классы сервлетов и реализуют метод getUserId , используя Java API Users. AppEngineDataStoreFactory (из google-http-client-appengine ) — хороший вариант для сохранения учетных данных с использованием API Google App Engine Data Store.
Пример взят (слегка изменен) из файла calendar-appengine-sample :
public class CalendarAppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // do stuff } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return Utils.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return Utils.newFlow(); } } class Utils { static String getRedirectUri(HttpServletRequest req) { GenericUrl url = new GenericUrl(req.getRequestURL().toString()); url.setRawPath("/oauth2callback"); return url.build(); } static GoogleAuthorizationCodeFlow newFlow() throws IOException { return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory( DATA_STORE_FACTORY).setAccessType("offline").build(); } } public class OAuth2Callback extends AbstractAppEngineAuthorizationCodeCallbackServlet { private static final long serialVersionUID = 1L; @Override protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential) throws ServletException, IOException { resp.sendRedirect("/"); } @Override protected void onError( HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse) throws ServletException, IOException { String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname(); resp.getWriter().print("<h3>" + nickname + ", why don't you want to play with me?</h1>"); resp.setStatus(200); resp.addHeader("Content-Type", "text/html"); } @Override protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException { return Utils.getRedirectUri(req); } @Override protected AuthorizationCodeFlow initializeFlow() throws IOException { return Utils.newFlow(); } }
Дополнительный пример см. в файле storage-serviceaccount-appengine-sample .
Служебные счета
GoogleCredential также поддерживает сервисные учетные записи . В отличие от учетных данных, при которых клиентское приложение запрашивает доступ к данным конечного пользователя, сервисные учетные записи предоставляют доступ к собственным данным клиентского приложения. Ваше клиентское приложение подписывает запрос на получение токена доступа, используя закрытый ключ, загруженный из консоли Google API .
Пример использования:
HttpTransport httpTransport = new NetHttpTransport(); JsonFactory jsonFactory = GsonFactory.getDefaultInstance(); ... // Build service account credential. GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json")) .createScoped(Collections.singleton(PlusScopes.PLUS_ME)); // Set up global Plus instance. plus = new Plus.Builder(httpTransport, jsonFactory, credential) .setApplicationName(APPLICATION_NAME).build(); ...
Дополнительный пример см. в файле storage-serviceaccount-cmdline-sample .
Выдача себя за другое лицо
Вы также можете использовать поток служебной учетной записи для имитации пользователя в принадлежащем вам домене. Это очень похоже на описанный выше поток служебной учетной записи, но дополнительно вы вызываете метод GoogleCredential.Builder.setServiceAccountUser(String) .
Установленные приложения
Это алгоритм авторизации через командную строку, описанный в разделе «Использование OAuth 2.0 для установленных приложений» .
Пример использования:
public static void main(String[] args) { try { httpTransport = new NetHttpTransport(); dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR); // authorization Credential credential = authorize(); // set up global Plus instance plus = new Plus.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName( APPLICATION_NAME).build(); // ... } private static Credential authorize() throws Exception { // load client secrets GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(PlusSample.class.getResourceAsStream("/client_secrets.json"))); // set up authorization code flow GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( httpTransport, JSON_FACTORY, clientSecrets, Collections.singleton(PlusScopes.PLUS_ME)).setDataStoreFactory( dataStoreFactory).build(); // authorize return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user"); }
Приложения на стороне клиента
Для использования описанного в разделе «Использование OAuth 2.0 для клиентских приложений» клиентского интерфейса на основе браузера обычно выполняются следующие шаги:
- Перенаправьте конечного пользователя в браузере на страницу авторизации, используя GoogleBrowserClientRequestUrl, чтобы предоставить вашему браузерному приложению доступ к защищенным данным конечного пользователя.
- Используйте клиентскую библиотеку Google API для JavaScript , чтобы обработать токен доступа, найденный во фрагменте URL-адреса по URI перенаправления, зарегистрированному в консоли Google API .
Пример использования в веб-приложении:
public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException { String url = new GoogleBrowserClientRequestUrl("812741506391.apps.googleusercontent.com", "https://oauth2.example.com/oauthcallback", Arrays.asList( "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile")).setState("/profile").build(); response.sendRedirect(url); }
Android
Какую библиотеку использовать с Android:
Если вы разрабатываете приложения для Android и используемый вами API Google включен в библиотеку Google Play Services , используйте именно эту библиотеку для достижения наилучшей производительности и удобства работы. Если используемый вами API Google для Android не входит в библиотеку Google Play Services, вы можете использовать клиентскую библиотеку Google API для Java, которая поддерживает Android 4.0 (Ice Cream Sandwich) (или выше) и описана здесь. Поддержка Android в клиентской библиотеке Google API для Java находится в стадии @Beta .
Фон:
Начиная с Eclair (SDK 2.1), управление учетными записями пользователей на устройстве Android осуществляется с помощью Account Manager. Вся авторизация приложений Android централизованно управляется SDK с помощью AccountManager . Вы указываете область действия OAuth 2.0, необходимую вашему приложению, и он возвращает токен доступа для использования.
Область действия OAuth 2.0 указывается с помощью параметра authTokenType как oauth2: плюс область действия. Например:
oauth2:https://www.googleapis.com/auth/tasks
Это определяет права доступа на чтение/запись к API Google Tasks. Если вам требуется несколько областей действия OAuth 2.0, используйте список, разделенный пробелами.
Некоторые API имеют специальные параметры authTokenType , которые также работают. Например, "Управление задачами" — это псевдоним для приведенного выше примера authtokenType .
Также необходимо указать ключ API в консоли Google API . В противном случае токен, предоставленный AccountManager, обеспечит вам только анонимную квоту, которая обычно очень мала. В отличие от этого, указав ключ API, вы получаете более высокую бесплатную квоту и можете по желанию настроить оплату за использование сверх неё.
Пример фрагмента кода взят из набора примеров tasks-android-sample :
com.google.api.services.tasks.Tasks service; @Override public void onCreate(Bundle savedInstanceState) { credential = GoogleAccountCredential.usingOAuth2(this, Collections.singleton(TasksScopes.TASKS)); SharedPreferences settings = getPreferences(Context.MODE_PRIVATE); credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null)); service = new com.google.api.services.tasks.Tasks.Builder(httpTransport, jsonFactory, credential) .setApplicationName("Google-TasksAndroidSample/1.0").build(); } private void chooseAccount() { startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_GOOGLE_PLAY_SERVICES: if (resultCode == Activity.RESULT_OK) { haveGooglePlayServices(); } else { checkGooglePlayServicesAvailable(); } break; case REQUEST_AUTHORIZATION: if (resultCode == Activity.RESULT_OK) { AsyncLoadTasks.run(this); } else { chooseAccount(); } break; case REQUEST_ACCOUNT_PICKER: if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) { String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME); if (accountName != null) { credential.setSelectedAccountName(accountName); SharedPreferences settings = getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putString(PREF_ACCOUNT_NAME, accountName); editor.commit(); AsyncLoadTasks.run(this); } } break; } }