Übersicht
Verwendung:In diesem Dokument werden die generischen OAuth 2.0-Funktionen beschrieben, die von der Google OAuth-Clientbibliothek für Java angeboten werden. Sie können diese Funktionen für die Authentifizierung und Autorisierung aller Internetdienste verwenden.
Eine Anleitung zur Verwendung von GoogleCredential
für die OAuth 2.0-Autorisierung mit Google-Diensten finden Sie unter OAuth 2.0 mit der Google API-Clientbibliothek für Java verwenden.
Zusammenfassung: OAuth 2.0 ist eine Standard-Spezifikation, mit der Endnutzer eine Clientanwendung sicher für den Zugriff auf geschützte serverseitige Ressourcen autorisieren können. Darüber hinaus wird in der OAuth 2.0-Anforderung für Trägertoken erläutert, wie Sie mit einem Zugriffstoken, das während des Autorisierungsvorgangs für Endnutzer gewährt wurde, auf diese geschützten Ressourcen zugreifen.
Weitere Informationen finden Sie in der Javadoc-Dokumentation für die folgenden Pakete:
- com.google.api.client.auth.oauth2 (von google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (aus google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Kundenregistrierung
Bevor Sie die Google OAuth-Clientbibliothek für Java verwenden können, müssen Sie Ihre Anwendung wahrscheinlich bei einem Autorisierungsserver registrieren, um eine Client-ID und einen Clientschlüssel zu erhalten. Allgemeine Informationen zu diesem Vorgang finden Sie in der Spezifikation zur Clientregistrierung.
Anmeldedaten und Anmeldedatenspeicher
Credential ist eine threadsichere OAuth 2.0-Hilfsklasse für den Zugriff auf geschützte Ressourcen mit einem Zugriffstoken. Bei Verwendung eines Aktualisierungstokens aktualisiert Credential
das Zugriffstoken mit dem Aktualisierungstoken auch bei Ablauf des Zugriffstokens. Wenn Sie beispielsweise bereits ein Zugriffstoken haben, können Sie eine Anfrage so stellen:
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(); }
Die meisten Anwendungen müssen das Zugriffstoken der Anmeldedaten beibehalten und das Aktualisierungstoken beibehalten, um eine Weiterleitung auf die Autorisierungsseite im Browser zu vermeiden. Die Implementierung von CredentialStore in dieser Bibliothek wird eingestellt und in zukünftigen Releases entfernt. Alternativ können Sie die Schnittstellen DataStoreFactory und DataStore mit StoredCredential verwenden, die von der Google HTTP-Clientbibliothek für Java bereitgestellt werden.
Sie können eine der folgenden von der Bibliothek bereitgestellten Implementierungen verwenden:
- JdoDataStoreFactory speichert die Anmeldedaten mit JDO.
- AppEngineDataStoreFactory speichert die Anmeldedaten mithilfe der Google App Engine Data Store API.
- MemoryDataStoreFactory speichert die Anmeldedaten im Arbeitsspeicher, was nur als kurzfristiger Speicher für die Lebensdauer des Prozesses nützlich ist.
- FileDataStoreFactory speichert die Anmeldedaten in einer Datei.
Google App Engine-Nutzer:
AppEngineCredentialStore wurde eingestellt und wird entfernt.
Wir empfehlen die Verwendung von AppEngineDataStoreFactory mit StoredCredential. Wenn Sie Anmeldedaten auf die alte Weise gespeichert haben, können Sie die hinzugefügten Hilfsmethoden migrateTo(AppEngineDataStoreFactory) oder migrateTo(DataStore) verwenden, um sie zu migrieren.
Verwenden Sie DataStoreCredentialRefreshListener und legen Sie ihn mithilfe von GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) für die Anmeldedaten fest.
Autorisierungscode-Ablauf
Verwenden Sie den Autorisierungscode-Ablauf, damit der Endnutzer Ihrer Anwendung Zugriff auf seine geschützten Daten gewähren kann. Das Protokoll für diesen Ablauf ist in der Spezifikation der Autorisierungscode-Erteilung angegeben.
Dieser Ablauf wird mit AuthorizationCodeFlow implementiert. Folgende Schritte sind auszuführen:
- Ein Endnutzer meldet sich in Ihrer Anwendung an. Dieser Nutzer muss einer Nutzer-ID zugeordnet werden, die für Ihre Anwendung eindeutig ist.
- Rufe AuthorizationCodeFlow.loadCredential(String) basierend auf der Nutzer-ID auf, um zu prüfen, ob die Anmeldedaten des Nutzers bereits bekannt sind. In diesem Fall ist der Vorgang für Sie abgeschlossen.
- Falls nicht, rufen Sie AuthorizationCodeFlow.newAuthorizationUrl() auf und leiten Sie den Browser des Endnutzers auf eine Autorisierungsseite weiter, auf der er Ihrer Anwendung Zugriff auf seine geschützten Daten gewähren kann.
- Der Webbrowser leitet Sie dann mit einem "Code"-Abfrageparameter an die Weiterleitungs-URL weiter, mit dem Sie dann mithilfe von AuthorizationCodeFlow.newTokenRequest(String) ein Zugriffstoken anfordern können.
- Verwenden Sie AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) zum Speichern und Abrufen von Anmeldedaten für den Zugriff auf geschützte Ressourcen.
Wenn Sie AuthorizationCodeFlow nicht verwenden, können Sie auch die Klassen der unteren Ebene verwenden:
- Verwenden Sie DataStore.get(String), um die Anmeldedaten basierend auf der User-ID aus dem Speicher zu laden.
- Verwenden Sie AuthorizationCodeRequestUrl, um den Browser zur Autorisierungsseite weiterzuleiten.
- Verwende AuthorizationCodeResponseUrl, um die Autorisierungsantwort zu verarbeiten und den Autorisierungscode zu parsen.
- Verwende AuthorizationCodeTokenRequest, um ein Zugriffstoken und gegebenenfalls ein Aktualisierungstoken anzufordern.
- Erstellen Sie eine neue Anmeldedatenanfrage und speichern Sie sie mit DataStore.set(String, V).
- Mit den Anmeldedaten auf geschützte Ressourcen zugreifen Abgelaufene Zugriffstokens werden gegebenenfalls automatisch mit dem Aktualisierungstoken aktualisiert. Verwenden Sie DataStoreCredentialRefreshListener und legen Sie ihn mit Credential.Builder.addRefreshListener(CredentialRefreshListener) für die Anmeldedaten fest.
Ablauf des Autorisierungscodes für Servlets
Diese Bibliothek bietet Servlet-Hilfsklassen, die den Ablauf des Autorisierungscodes für grundlegende Anwendungsfälle erheblich vereinfachen. Sie müssen lediglich konkrete Unterklassen von AbstractAuthorizationCodeServlet und AbstractAuthorizationCodeCallbackServlet (aus google-oauth-client-servlet) angeben und sie der Datei „web.xml“ hinzufügen. Sie müssen jedoch weiterhin das Nutzer-Log-in für Ihre Webanwendung verwalten und eine User-ID extrahieren.
Beispielcode:
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 } }
Autorisierungscode-Ablauf der Google App Engine
Der Autorisierungscodeablauf in der App Engine ist fast identisch mit dem Autorisierungscodeablauf für Servlets, mit der Ausnahme, dass wir die Users Java API der Google App Engine nutzen können. Damit die Java API für Nutzer aktiviert werden kann, muss der Nutzer angemeldet sein. Informationen zum Weiterleiten von Nutzern zu einer Anmeldeseite, wenn sie noch nicht angemeldet sind, finden Sie unter Sicherheit und Authentifizierung (in web.xml).
Der Hauptunterschied zum Servlet-Fall besteht darin, dass Sie konkrete Unterklassen von AbstractAppEngineAuthorizationCodeServlet und AbstractAppEngineAuthorizationCodeCallbackServlet (aus google-oauth-client-appengine) angeben. Sie erweitern die abstrakten Servlet-Klassen und implementieren die getUserId
-Methode für Sie mithilfe der Java API für Nutzer. AppEngineDataStoreFactory (aus der Google HTTP-Clientbibliothek für Java) ist eine gute Option, um die Anmeldedaten mit der Google App Engine Data Store API zu speichern.
Beispielcode:
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(); } }
Autorisierungscode-Vorgang für die Befehlszeile
Vereinfachter Beispielcode aus 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); ... }
Browserbasierter Clientablauf
Dies sind die typischen Schritte des browserbasierten Client-Ablaufs, die in der Spezifikation für die implizite Autorisierung angegeben sind:
- Leite den Browser des Endnutzers mit BrowserClientRequestUrl zur Autorisierungsseite weiter, auf der der Endnutzer deiner Anwendung Zugriff auf seine geschützten Daten gewähren kann.
- Verwenden Sie eine JavaScript-Anwendung, um das Zugriffstoken zu verarbeiten, das sich im URL-Fragment am Weiterleitungs-URI befindet, der beim Autorisierungsserver registriert ist.
Verwendungsbeispiel bei einer Webanwendung:
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); }
Abgelaufenes Zugriffstoken erkennen
Gemäß der OAuth 2.0-Bearer-Spezifikation antwortet der Server in der Regel mit einem HTTP-401 Unauthorized
-Statuscode wie dem folgenden, wenn der Server aufgerufen wird, um mit einem abgelaufenen Zugriffstoken auf eine geschützte Ressource zuzugreifen:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Die Spezifikation scheint jedoch sehr flexibel zu sein. Weitere Informationen findest du in der Dokumentation des OAuth 2.0-Anbieters.
Alternativ kannst du den Parameter expires_in
in der Antwort zum Zugriffstoken prüfen.
Hier wird die Lebensdauer des gewährten Zugriffstokens in Sekunden angegeben, die in der Regel eine Stunde beträgt. Unter Umständen läuft das Zugriffstoken am Ende dieses Zeitraums jedoch nicht ab und der Server lässt den Zugriff weiterhin zu. Daher empfehlen wir in der Regel, auf einen 401 Unauthorized
-Statuscode zu warten, anstatt davon auszugehen, dass das Token aufgrund der verstrichenen Zeit abgelaufen ist. Alternativ können Sie versuchen, ein Zugriffstoken kurz vor Ablauf zu aktualisieren. Wenn der Tokenserver nicht verfügbar ist, verwenden Sie das Zugriffstoken so lange, bis Sie eine 401
erhalten. Das ist die Standardstrategie in Credential.
Eine weitere Möglichkeit besteht darin, vor jeder Anfrage ein neues Zugriffstoken zu erhalten. Dafür ist jedoch jedes Mal eine zusätzliche HTTP-Anfrage an den Tokenserver erforderlich, sodass die Wahl in Bezug auf Geschwindigkeit und Netzwerknutzung wahrscheinlich schlecht ist. Idealerweise wird das Zugriffstoken in einem sicheren, persistenten Speicher gespeichert, um die Anzahl der Anfragen einer Anwendung nach neuen Zugriffstokens zu minimieren. Bei installierten Anwendungen ist der sichere Speicher jedoch ein schwieriges Problem.
Ein Zugriffstoken kann auch aus anderen Gründen ungültig werden als nur aufgrund des Ablaufdatums. Das ist beispielsweise der Fall, wenn der Nutzer das Token ausdrücklich widerrufen hat. Achten Sie daher darauf, dass Ihr Code zur Fehlerbehandlung robust ist. Wenn Sie feststellen, dass ein Token nicht mehr gültig ist, z. B. weil es abgelaufen oder widerrufen wurde, müssen Sie das Zugriffstoken aus Ihrem Speicher entfernen. Unter Android müssen Sie beispielsweise AccountManager.invalidateAuthToken aufrufen.