Omówienie
Cel: w tym dokumencie opisano ogólne funkcje OAuth 2.0 oferowane przez i bibliotekę klienta Google OAuth do języka Java. Możesz użyć tych funkcji do: uwierzytelnianie i autoryzacja w przypadku usług internetowych.
Instrukcje używania interfejsu GoogleCredential
do autoryzacji OAuth 2.0
usług Google, zobacz
Używanie protokołu OAuth 2.0 z biblioteką klienta interfejsów API Google dla języka Java.
Podsumowanie: OAuth 2.0 to standardowa specyfikacja umożliwiająca użytkownikom bezpieczną autoryzację klienta w celu uzyskania dostępu do chronionych zasobów po stronie serwera. Dodatkowo Token okaziciela OAuth 2.0 wyjaśnia, jak uzyskać dostęp do tych chronionych zasobów za pomocą token przyznawany podczas procesu autoryzacji użytkownika.
Szczegółowe informacje znajdziesz w dokumentacji Javadoc dotyczącej tych pakietów:
- com.google.api.client.auth.oauth2 (z google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (from google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Rejestracja klienta
Przed użyciem biblioteki klienta Google OAuth dla języka Java zarejestrować aplikację na serwerze autoryzacji w celu otrzymania identyfikatora klienta i tajny klucz klienta. (Ogólne informacje o tym procesie znajdziesz w Klient specyfikacja rejestracji).
Magazyn danych logowania
Dane logowania
to klasa pomocnicza protokołu OAuth 2.0 działająca w wątkach, która umożliwia dostęp do zabezpieczonych zasobów za pomocą
token dostępu. Gdy używasz tokena odświeżania, Credential
odświeża też dostęp
gdy token dostępu wygaśnie przy użyciu tokena odświeżania. Jeśli na przykład
masz już token dostępu, możesz wysłać żądanie w ten sposób:
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
Większość aplikacji musi zachować token dostępu danych logowania, odśwież token, aby w przyszłości uniknąć przekierowania do autoryzacji w przeglądarce. CredentialStore implementacja w tej bibliotece została wycofana i w przyszłości zostanie usunięta wersji. Alternatywnym rozwiązaniem jest użycie DataStoreFactory i DataStore z interfejsami StoredCredential, udostępniane przez Biblioteka klienta HTTP Google do języka Java.
Możesz użyć jednej z tych implementacji udostępnianych przez bibliotekę:
- JdoDataStoreFactory zachowa dane logowania za pomocą JDO.
- AppEngineDataStoreFactory nadal zachowuje dane logowania przy użyciu interfejsu Google App Engine Data Store API.
- MemoryDataStoreFactory „utrwala” danych uwierzytelniających w pamięci, które są przydatne tylko jako krótkoterminowe i przechowywania danych w całym cyklu życia tego procesu.
- FileDataStoreFactory zachowa dane logowania w pliku.
Użytkownicy Google App Engine:
Interfejs AppEngineCredentialStore został wycofany i jest on usuwany.
Zalecamy użycie ciągu AppEngineDataStoreFactory za pomocą danych StoredCredential. Jeśli masz dane logowania zapisane w dotychczasowy sposób, możesz użyć dodanych metod pomocniczych migrateTo(AppEngineDataStoreFactory) lub migrateTo(DataStore) do migracji.
Użyj DataStoreCredentialRefreshListener. i ustaw go dla danych logowania za pomocą GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Przepływ kodu autoryzacji
Użyj przepływu kodu autoryzacji, aby umożliwić użytkownikowi końcowemu przyznania Twojej aplikacji. do ich chronionych danych. Protokół tego procesu jest określony w sekcji Specyfikacja przyznania kodu autoryzacji.
Implementacja tego procesu odbywa się za pomocą AuthorizationCodeFlow. Kroki:
- Użytkownik loguje się do Twojej aplikacji. Musisz powiązać tego użytkownika z unikalny identyfikator użytkownika aplikacji.
- Zadzwoń do nas AuthorizationCodeFlow.loadCredential(String), według identyfikatora użytkownika, aby sprawdzić, czy dane logowania użytkownika są już znane. Jeśli tak, to już wszystko.
- Jeśli nie, wywołaj AuthorizationCodeFlow.newAuthorizationUrl(). i skierować przeglądarkę użytkownika na stronę autoryzacji, na której może dostępu aplikacji do ich chronionych danych.
- Następnie przeglądarka przekierowuje użytkownika za pomocą „kodu” zapytanie za pomocą którego można wysłać żądanie tokena dostępu za pomocą AuthorizationCodeFlow.newTokenRequest(String).
- Używaj AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) aby przechowywać i uzyskiwać dane logowania umożliwiające dostęp do zabezpieczonych zasobów.
Jeśli nie używasz AuthorizationCodeFlow, możesz użyć klas niższego poziomu:
- Użyj DataStore.get(String). aby wczytać dane logowania z magazynu w oparciu o identyfikator użytkownika.
- Użyj AuthorizationCodeRequestUrl aby przekierować przeglądarkę na stronę autoryzacji.
- Użyj AuthorizationCodeResponseUrl. do przetworzenia odpowiedzi autoryzacji i przeanalizowania kodu autoryzacji.
- Użyj AuthorizationCodeTokenRequest o token dostępu i ewentualne tokeny odświeżania.
- Utwórz nowe dane logowania i zapisz je w elemencie DataStore.set(String, V).
- Uzyskaj dostęp do zabezpieczonych zasobów za pomocą danych logowania. Wygasłe tokeny dostępu są automatycznie odświeżane za pomocą tokena odświeżania, jeśli mają zastosowanie. Pamiętaj, aby użyć DataStoreCredentialRefreshListener i ustaw go dla danych logowania za pomocą Credential.Builder.addRefreshListener(CredentialRefreshListener).
Przepływ kodu autoryzacji serwlet
Ta biblioteka udostępnia klasy pomocnicze serwletów, które znacznie upraszczają procedury kodu autoryzacji do podstawowych zastosowań. Wystarczy, że podasz konkretne podklasy z AbstractAuthorizationCodeServlet i AbstractAuthorizationCodeCallbackServlet (z google-oauth-client-servlet) i dodaj je do pliku web.xml. Pamiętaj, że w dalszym ciągu musisz zadbać o użytkownika login w aplikacji internetowej i wyodrębnij identyfikator użytkownika.
Przykładowy kod:
public class ServletSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Przepływ kodu autoryzacji Google App Engine
Przepływ kodu autoryzacji w App Engine jest prawie taki sam jak serwlet przepływu kodu autoryzacji, z tym wyjątkiem, że możemy korzystać z mechanizmu Użytkownicy interfejsu Java API. Aby można było włączyć interfejs Users Java API, użytkownik musi być zalogowany. w przypadku informacje na temat przekierowywania użytkowników na stronę logowania, jeśli jeszcze tego nie robią; zalogowany, zobacz Zabezpieczenia i uwierzytelnianie (w pliku web.xml).
Podstawową różnicą w porównaniu z przypadkiem serwletu jest fakt, że podajesz
podklasy
AbstractAppEngineAuthorizationCodeServlet i AbstractAppEngineAuthorizationCodeCallbackServlet (z google-oauth-client-appengine). Rozszerzają one abstrakcyjne klasy serwletów i implementują metodę getUserId
za pomocą interfejsu User Java API. AppEngineDataStoreFactory (z Biblioteki klienta HTTP Google dla Javy to dobry sposób na utrwalenie danych logowania przy użyciu interfejsu Google App Engine Data Store API.
Przykładowy kod:
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
Przepływ kodu autoryzacji wiersza poleceń
Uproszczony przykładowy kod pobrany z dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
Przepływ klienta w przeglądarce
Są to typowe kroki przepływu klienta w przeglądarce określone w Specyfikacja niejawnego zezwolenia:
- Korzystając z metody BrowserClientRequestUrl, i przekierować przeglądarkę użytkownika na stronę autoryzacji, na której może on przyznać aplikacji dostęp do ich chronionych danych.
- Użyj aplikacji JavaScript do przetworzenia tokena dostępu znalezionego w adresie URL w identyfikatorze URI przekierowania zarejestrowanym na serwerze autoryzacji.
Przykład użycia aplikacji internetowej:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
Wykrywanie wygasłego tokena dostępu
Zgodnie ze specyfikacją okaziciela protokołu OAuth 2.0
gdy serwer jest wywoływany w celu uzyskania dostępu do chronionego zasobu z wygasłym dostępem
token, serwer zwykle odpowiada kodem stanu HTTP 401 Unauthorized
na przykład:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Wydaje się jednak, że specyfikacja ma dużą elastyczność. Dla: zapoznaj się z dokumentacją dostawcy OAuth 2.0.
Możesz też sprawdzić parametr expires_in
w pliku
odpowiedź tokena dostępu.
Określa czas trwania (w sekundach) przyznanego tokena dostępu, który jest
zwykle w ciągu godziny. Jednak token dostępu może nie wygasnąć na końcu.
i serwer może nadal zezwalać na dostęp. Dlatego
zwykle zalecamy zaczekać na kod stanu 401 Unauthorized
, a nie
przy założeniu, że token wygasł na podstawie czasu, który upłynął. Ewentualnie możesz
spróbuj odświeżyć token dostępu krótko przed jego wygaśnięciem. Jeśli serwer tokenów
jest niedostępny, używaj tokena dostępu, aż otrzymasz 401
. Ten
to strategia używana domyślnie
Dane logowania.
Inną opcją jest uzyskanie nowego tokena dostępu przed każdym żądaniem, ale wymaga dodatkowego żądania HTTP do serwera tokenów za każdym razem, więc słaby wybór pod względem szybkości i wykorzystania sieci. Token dostępu najlepiej przechowywać w bezpiecznej, trwałej pamięci masowej w celu zminimalizowania liczby żądań nowego dostępu wysyłanych przez aplikację tokeny. (Jednak w przypadku zainstalowanych aplikacji bezpieczne przechowywanie danych to trudny problem).
Pamiętaj, że token dostępu może stać się nieważny z innych powodów niż jego wygaśnięcie, na przykład w sytuacji, gdy użytkownik wyraźnie unieważnił token, upewnij się więc, jest rozbudowany. Gdy wykryjesz, że token nie jest już ważne, np. po wygaśnięciu lub unieważnieniu, należy usunąć dostęp z Twojego miejsca na dane. Na przykład na Androidzie musisz wywołać AccountManager.invalidateAuthToken,