Przegląd
Cel: w tym dokumencie wyjaśniamy, jak używać klasy narzędziowej GoogleCredential do autoryzacji OAuth 2.0 w usługach Google. Informacje o ogólnych funkcjach OAuth 2.0, które udostępniamy, znajdziesz w artykule OAuth 2.0 i biblioteka klienta Google OAuth dla języka Java.
Podsumowanie: aby uzyskać dostęp do chronionych danych przechowywanych w usługach Google, używaj OAuth 2.0 do autoryzacji. Interfejsy API Google obsługują procesy OAuth 2.0 w przypadku różnych typów aplikacji klienckich. We wszystkich tych procesach aplikacja kliencka żąda tokena dostępu, który jest powiązany tylko z nią i właścicielem chronionych danych, do których uzyskuje dostęp. Token dostępu jest też powiązany z ograniczonym zakresem, który określa rodzaj danych, do których ma dostęp aplikacja kliencka (np. „Zarządzaj swoimi zadaniami”). Ważnym celem protokołu OAuth 2.0 jest zapewnienie bezpiecznego i wygodnego dostępu do chronionych danych przy jednoczesnym zminimalizowaniu potencjalnych skutków kradzieży tokena dostępu.
Pakiety OAuth 2.0 w bibliotece klienta interfejsu API Google dla języka Java są oparte na ogólnej bibliotece klienta Google OAuth 2.0 dla języka Java.
Szczegółowe informacje znajdziesz w dokumentacji Javadoc tych pakietów:
- com.google.api.client.googleapis.auth.oauth2 (z google-api-client)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (z google-api-client-appengine)
Konsola interfejsów API Google
Zanim uzyskasz dostęp do interfejsów API Google, musisz skonfigurować projekt w Konsoli interfejsów API Google na potrzeby autoryzacji i płatności, niezależnie od tego, czy Twój klient jest zainstalowaną aplikacją, aplikacją mobilną, serwerem internetowym czy klientem działającym w przeglądarce.
Instrukcje prawidłowego konfigurowania danych logowania znajdziesz w Pomocy Konsoli interfejsów API.
Dane logowania
GoogleCredential
GoogleCredential to bezpieczna wątkowo klasa pomocnicza protokołu OAuth 2.0, która umożliwia dostęp do chronionych zasobów za pomocą tokena dostępu. Jeśli na przykład masz już token dostępu, możesz wysłać żądanie w ten sposób:
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Plus plus = new Plus.builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), credential) .setApplicationName("Google-PlusSample/1.0") .build();
Tożsamość Google App Engine
Te alternatywne dane logowania są oparte na interfejsie Google App Engine App Identity Java API. W przeciwieństwie do danych logowania, w przypadku których aplikacja kliencka prosi o dostęp do danych użytkownika, interfejs App Identity API zapewnia dostęp do danych samej aplikacji klienckiej.
Użyj AppIdentityCredential (z pakietu google-api-client-appengine). Ten rodzaj danych logowania jest znacznie prostszy, ponieważ Google App Engine zajmuje się wszystkimi szczegółami. Określasz tylko potrzebny zakres OAuth 2.0.
Przykładowy kod pochodzący z 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(); }
Magazyn danych
Token dostępu zwykle wygasa po godzinie. Jeśli spróbujesz go użyć po tym czasie, pojawi się błąd.
Klasa GoogleCredential automatycznie „odświeża” token, co oznacza po prostu pobieranie nowego tokena dostępu. Odbywa się to za pomocą tokena odświeżania o długim okresie ważności, który jest zwykle otrzymywany wraz z tokenem dostępu, jeśli podczas przepływu kodu autoryzacji używasz parametru access_type=offline (patrz GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).
Większość aplikacji będzie musiała przechowywać token dostępu lub token odświeżania danych logowania. Aby zachować tokeny dostępu lub odświeżania danych logowania, możesz podać własną implementację interfejsu DataStoreFactory z interfejsem StoredCredential lub użyć jednej z tych implementacji udostępnianych przez bibliotekę:
- AppEngineDataStoreFactory: zapisuje dane logowania za pomocą interfejsu Google App Engine Data Store API.
- MemoryDataStoreFactory: „utrwala” dane logowania w pamięci, co jest przydatne tylko jako krótkoterminowe miejsce przechowywania przez cały czas trwania procesu.
- FileDataStoreFactory: zapisuje dane logowania w pliku.
Użytkownicy App Engine: AppEngineCredentialStore został wycofany i wkrótce zostanie usunięty. Zalecamy używanie AppEngineDataStoreFactory z StoredCredential. Jeśli masz dane logowania zapisane w starym formacie, możesz użyć dodatkowych metod pomocniczych migrateTo(AppEngineDataStoreFactory) lub migrateTo(DataStore), aby przeprowadzić migrację.
Możesz użyć interfejsu DataStoreCredentialRefreshListener i ustawić go dla danych logowania za pomocą metody GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Przepływ kodu autoryzacji
Użyj procesu kodu autoryzacji, aby umożliwić użytkownikowi końcowemu przyznanie aplikacji dostępu do chronionych danych w interfejsach API Google. Protokół tego procesu jest określony w sekcji Authorization Code Grant (Uwierzytelnianie za pomocą kodu autoryzacji).
Ten przepływ jest realizowany za pomocą klasy GoogleAuthorizationCodeFlow. Kroki:
- Użytkownik loguje się w Twojej aplikacji. Musisz powiązać tego użytkownika z identyfikatorem użytkownika, który jest unikalny dla Twojej aplikacji.
- Wywołaj funkcję AuthorizationCodeFlow.loadCredential(String) na podstawie identyfikatora użytkownika, aby sprawdzić, czy dane logowania użytkownika są już znane. Jeśli tak, to wszystko.
- Jeśli nie, wywołaj funkcję AuthorizationCodeFlow.newAuthorizationUrl() i przekieruj przeglądarkę użytkownika na stronę autoryzacji, aby przyznać aplikacji dostęp do jego chronionych danych.
- Serwer autoryzacji Google przekieruje przeglądarkę z powrotem na adres URL przekierowania określony przez aplikację, wraz z parametrem zapytania
code. Użyj parametrucode, aby poprosić o token dostępu za pomocą funkcji AuthorizationCodeFlow.newTokenRequest(String)). - Użyj metody AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String)), aby zapisać i uzyskać dane logowania umożliwiające dostęp do chronionych zasobów.
Jeśli nie używasz GoogleAuthorizationCodeFlow, możesz użyć klas niższego poziomu:
- Użyj funkcji DataStore.get(String), aby wczytać dane logowania z magazynu na podstawie identyfikatora użytkownika.
- Użyj GoogleAuthorizationCodeRequestUrl, aby przekierować przeglądarkę na stronę autoryzacji.
- Użyj AuthorizationCodeResponseUrl, aby przetworzyć odpowiedź autoryzacji i przeanalizować kod autoryzacji.
- Użyj GoogleAuthorizationCodeTokenRequest, aby poprosić o token dostępu i ewentualnie token odświeżania.
- Utwórz nowe GoogleCredential i zapisz je za pomocą DataStore.set(String, V).
- Uzyskaj dostęp do chronionych zasobów za pomocą
GoogleCredential. Wygasłe tokeny dostępu zostaną automatycznie odświeżone przy użyciu tokena odświeżania (w stosownych przypadkach). Użyj DataStoreCredentialRefreshListener i ustaw go dla danych logowania za pomocą GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).
Podczas konfigurowania projektu w Konsoli interfejsów API Google wybierasz różne dane logowania w zależności od używanego procesu. Więcej informacji znajdziesz w artykułach Konfigurowanie OAuth 2.0 i Scenariusze OAuth 2.0. Poniżej znajdziesz fragmenty kodu dla każdego z tych procesów.
Aplikacje serwera WWW
Protokół tego procesu jest opisany w artykule Używanie OAuth 2.0 w aplikacjach udostępnianych przez serwer WWW.
Ta biblioteka zawiera klasy pomocnicze servletów, które znacznie upraszczają przepływ kodu autoryzacji w przypadku podstawowych zastosowań. Wystarczy, że podasz konkretne podklasy AbstractAuthorizationCodeServlet i AbstractAuthorizationCodeCallbackServlet (z google-oauth-client-servlet) i dodasz je do pliku web.xml. Pamiętaj, że nadal musisz zadbać o logowanie użytkowników w aplikacji internetowej i wyodrębnianie identyfikatora użytkownika.
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 } }
aplikacje Google App Engine,
Przepływ kodu autoryzacji w App Engine jest niemal identyczny z przepływem kodu autoryzacji w serwlecie, z tym wyjątkiem, że możemy korzystać z interfejsu Users Java API Google App Engine. Aby włączyć interfejs Users Java API, użytkownik musi być zalogowany. Informacje o przekierowywaniu użytkowników na stronę logowania, jeśli nie są jeszcze zalogowani, znajdziesz w sekcji Security and Authentication (w pliku web.xml).
Główna różnica w porównaniu z servletami polega na tym, że udostępniasz konkretne podklasy AbstractAppEngineAuthorizationCodeServlet i AbstractAppEngineAuthorizationCodeCallbackServlet (z google-oauth-client-appengine).
Rozszerzają one abstrakcyjne klasy servletów i implementują za Ciebie metodę getUserId za pomocą interfejsu Users Java API. AppEngineDataStoreFactory (z google-http-client-appengine) to dobra opcja do utrwalania danych logowania za pomocą interfejsu Google App Engine Data Store API.
Przykład zaczerpnięty (nieznacznie zmodyfikowany) z 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(); } }
Dodatkowy przykład znajdziesz w artykule storage-serviceaccount-appengine-sample.
Konta usługi
GoogleCredential obsługuje też konta usługi. W przeciwieństwie do danych logowania, za pomocą których aplikacja kliencka żąda dostępu do danych użytkownika, konta usługi zapewniają dostęp do danych samej aplikacji klienckiej. Aplikacja kliencka podpisuje żądanie tokena dostępu za pomocą klucza prywatnego pobranego z konsoli interfejsów API Google.
Przykład użycia:
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(); ...
Dodatkowy przykład znajdziesz w artykule storage-serviceaccount-cmdline-sample.
Podszywanie się pod inne osoby
Możesz też użyć przepływu konta usługi, aby podszywać się pod użytkownika w domenie, która należy do Ciebie. Jest to bardzo podobne do procesu konta usługi opisanego powyżej, ale dodatkowo wywołujesz funkcję GoogleCredential.Builder.setServiceAccountUser(String).
Zainstalowane aplikacje
Jest to przepływ kodu autoryzacji wiersza poleceń opisany w artykule Używanie OAuth 2.0 w przypadku zainstalowanych aplikacji.
Przykład użycia:
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"); }
Aplikacje po stronie klienta
Aby użyć przepływu klienta opartego na przeglądarce opisanego w artykule Korzystanie z OAuth 2.0 w przypadku aplikacji działających po stronie klienta, zwykle wykonujesz te czynności:
- Przekieruj użytkownika w przeglądarce na stronę autoryzacji za pomocą adresu GoogleBrowserClientRequestUrl, aby przyznać aplikacji przeglądarki dostęp do chronionych danych użytkownika.
- Użyj biblioteki klienta interfejsu API Google dla JavaScriptu, aby przetworzyć token dostępu znaleziony we fragmencie adresu URL w adresie URI przekierowania zarejestrowanym w Konsoli interfejsów API Google.
Przykładowe użycie w aplikacji internetowej:
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
Której biblioteki używać na Androidzie:
Jeśli tworzysz aplikację na Androida, a interfejs API Google, którego chcesz użyć, jest zawarty w bibliotece Usług Google Play, użyj tej biblioteki, aby uzyskać najlepszą wydajność i najlepsze wrażenia. Jeśli interfejs API Google, którego chcesz używać na Androidzie, nie jest częścią biblioteki Usług Google Play, możesz użyć biblioteki klienta interfejsu API Google dla języka Java, która obsługuje Androida 4.0 (Ice Cream Sandwich) lub nowszego. Opis tej biblioteki znajdziesz tutaj. Obsługa Androida w bibliotece klienta interfejsu API Google dla języka Java jest w wersji @Beta.
Informacje ogólne:
Od wersji Eclair (SDK 2.1) konta użytkowników są zarządzane na urządzeniu z Androidem za pomocą Menedżera kont. Cała autoryzacja aplikacji na Androida jest zarządzana centralnie przez pakiet SDK za pomocą interfejsu AccountManager. Określasz zakres OAuth 2.0, którego potrzebuje Twoja aplikacja, a usługa zwraca token dostępu do użycia.
Zakres OAuth 2.0 jest określany za pomocą parametru authTokenType jako oauth2:
plus zakres. Na przykład:
oauth2:https://www.googleapis.com/auth/tasks
Określa uprawnienia do odczytu i zapisu interfejsu Google Tasks API. Jeśli potrzebujesz kilku zakresów OAuth 2.0, użyj listy rozdzielonej spacjami.
Niektóre interfejsy API mają specjalne parametry authTokenType, które też działają. Na przykład „Zarządzaj zadaniami” to alias dla authtokenType przykładu pokazanego powyżej.
Musisz też podać klucz interfejsu API z Konsoli interfejsów API Google. W przeciwnym razie token, który otrzymasz od AccountManager, zapewni Ci tylko anonimowy limit, który jest zwykle bardzo niski. Z kolei podanie klucza interfejsu API zapewnia wyższy bezpłatny limit, a opcjonalnie możesz też skonfigurować płatności za użycie powyżej tego limitu.
Przykładowy fragment kodu z 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; } }