Обзор
Цель. В этом документе объясняется, как использовать служебный класс 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 в клиентской библиотеке API Google для Java созданы на основе универсальной клиентской библиотеки Google OAuth 2.0 для 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 Console
Чтобы получить доступ к API Google, вам нужно настроить проект в Google API Console для аутентификации и оплаты. Это необходимо сделать независимо от того, какой клиент вы используете: установленное приложение, мобильное приложение, веб-сервер или клиент, который работает в браузере.
Инструкции по настройке учетных данных можно найти в Справочном центре API Console.
Учетные данные
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
Эти альтернативные учетные данные основаны на Google App Engine App Identity Java API. В отличие от учетных данных, с помощью которых клиентское приложение запрашивает доступ к данным конечного пользователя, App Identity API предоставляет доступ к собственным данным клиентского приложения.
Используйте 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(); }
Хранилище данных
Обычно срок действия токена доступа составляет один час. Если вы попытаетесь использовать его по истечении этого времени, то получите сообщение об ошибке.
GoogleCredential автоматически обновляет токен, то есть получает новый токен доступа. Для этого используется токен обновления с длительным сроком действия, который обычно получается вместе с токеном доступа, если вы используете параметр access_type=offline во время обработки кода авторизации (см. GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).
Большинству приложений необходимо сохранять токен доступа и/или токен обновления учетных данных. Чтобы сохранить токены доступа и/или токены обновления учетных данных, вы можете предоставить собственную реализацию DataStoreFactory с StoredCredential или использовать одну из следующих реализаций, предоставляемых библиотекой:
- AppEngineDataStoreFactory сохраняет учетные данные с помощью Google App Engine Data Store API.
- 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 Console вы выбираете учетные данные в зависимости от используемого процесса. Подробнее о настройке 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 почти идентична обработке кода авторизации в сервлете, за исключением того, что мы можем использовать Users Java API Google App Engine. Чтобы включить Users Java API, пользователь должен войти в систему. О том, как перенаправить пользователя на страницу входа, если он ещё не вошел в систему, читайте в разделе Безопасность и аутентификация (в web.xml).
Основное отличие от случая с сервлетом заключается в том, что вы предоставляете конкретные подклассы AbstractAppEngineAuthorizationCodeServlet и AbstractAppEngineAuthorizationCodeCallbackServlet (из google-oauth-client-appengine).
Они расширяют абстрактные классы сервлетов и реализуют метод getUserId с помощью Users Java API. AppEngineDataStoreFactory (из google-http-client-appengine) – хороший вариант для сохранения учетных данных с помощью Google App Engine Data Store API.
Пример взят (с небольшими изменениями) из 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 Console.
Пример использования:
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 Console.
Пример использования для веб-приложения:
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, используйте эту библиотеку, чтобы обеспечить максимальную производительность и удобство работы. Если API Google, который вы хотите использовать с Android, не входит в библиотеку сервисов Google Play, вы можете использовать клиентскую библиотеку API Google для Java, которая поддерживает Android 4.0 (Ice Cream Sandwich) или более поздней версии и описана здесь. Поддержка Android в клиентской библиотеке API Google для Java находится на стадии бета-тестирования.
Определения
Начиная с версии Eclair (SDK 2.1), аккаунты пользователей на устройствах Android управляются с помощью Менеджера аккаунтов. Авторизация всех приложений для Android централизованно управляется SDK с помощью AccountManager. Вы указываете область действия OAuth 2.0, которая требуется вашему приложению, и получаете токен доступа.
Область действия OAuth 2.0 указывается в параметре authTokenType как oauth2: плюс область действия. Пример:
oauth2:https://www.googleapis.com/auth/tasks
Этот параметр определяет доступ для чтения и записи к Google Tasks API. Если вам нужно несколько областей действия OAuth 2.0, укажите их через пробел.
В некоторых API есть специальные параметры authTokenType, которые также работают. Например, "Управляйте задачами" – это псевдоним для примера authtokenType, показанного выше.
Также необходимо указать ключ API из Google API Console. В противном случае токен, который вы получите от 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; } }