OAuth 2.0 und die Google OAuth-Clientbibliothek für Java

Übersicht

Zweck:In diesem Dokument werden die generischen OAuth 2.0-Funktionen von der Google OAuth-Clientbibliothek für Java Sie können diese Funktionen für Authentifizierung und Autorisierung für Internetdienste.

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 ein Standardspezifikation, die es Endnutzern ermöglicht, einen Client sicher zu autorisieren um auf geschützte serverseitige Ressourcen zuzugreifen. Darüber hinaus enthält der OAuth 2.0-Inhabertoken wird erläutert, wie der Zugriff auf diese geschützten Ressourcen Token, das während der Endnutzerautorisierung gewährt wurde.

Weitere Informationen finden Sie in der Javadoc-Dokumentation für die folgenden Pakete:

Kundenregistrierung

Bevor Sie die Google OAuth-Clientbibliothek für Java verwenden, müssen Sie Ihre Anwendung bei einem Autorisierungsserver registrieren, um eine Client-ID und Clientschlüssel. (Allgemeine Informationen zu diesem Vorgang finden Sie in der Kunde Registrierungsspezifikation.

Anmeldedaten- und Anmeldedatenspeicher

Qualifikation ist eine Thread-sichere OAuth 2.0-Hilfsklasse für den Zugriff auf geschützte Ressourcen mithilfe eines ein Zugriffstoken hinzufügen. Bei Verwendung eines Aktualisierungstokens aktualisiert Credential auch den Zugriff Token, wenn das Zugriffstoken abläuft, verwenden Sie dazu das Aktualisierungstoken. Wenn Sie beispielsweise bereits über ein Zugriffstoken verfügt, können Sie so eine Anfrage 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 Zugriffs-Token der Anmeldedaten Aktualisierungs-Token, um eine künftige Weiterleitung zur Autorisierung zu vermeiden Seite im Browser. Die CredentialStore -Implementierung in dieser Bibliothek wurde verworfen und wird in Zukunft entfernt Veröffentlichungen. Alternativ können Sie die Methode DataStoreFactory und DataStore Benutzeroberflächen mit StoredCredential die vom Google HTTP-Clientbibliothek für Java

Sie können eine der folgenden von der Bibliothek bereitgestellten Implementierungen verwenden:

Google App Engine-Nutzer:

AppEngineCredentialStore wurde verworfen und wird entfernt.

Wir empfehlen die Verwendung von AppEngineDataStoreFactory mit StoredCredential. Wenn Sie Anmeldedaten auf die alte Weise gespeichert haben, können Sie die zusätzlichen Hilfsmethoden verwenden. migrateTo(AppEngineDataStoreFactory) oder migrateTo(DataStore) zu migrieren.

DataStoreCredentialRefreshListener verwenden und legen Sie ihn für die Anmeldedaten GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).

Vorgang mit Autorisierungscode

Verwenden Sie den Vorgang mit Autorisierungscode, damit der Endnutzer Ihre Anwendung erteilen kann Zugriff auf ihre geschützten Daten haben. Das Protokoll für diesen Ablauf wird in der Spezifikation der Autorisierungscode-Erteilung

Dieser Ablauf wird mithilfe von AuthorizationCodeFlow. Folgende Schritte sind auszuführen:

Wenn Sie nicht die AuthorizationCodeFlow können Sie die untergeordneten Klassen verwenden:

Vorgang mit Servlet-Autorisierungscode

Diese Bibliothek bietet Servlet-Hilfsklassen zur erheblichen Vereinfachung der Autorisierungscode-Vorgang für grundlegende Anwendungsfälle. Sie stellen einfach konkrete Unterklassen bereit, von AbstractAuthorizationCodeServlet und AbstractAuthorizationCodeCallbackServlet (von google-oauth-client-servlet) und fügen sie der Datei „web.xml“ hinzu. Sie müssen sich trotzdem noch um die melden Sie sich für Ihre Webanwendung an und extrahieren Sie eine Nutzer-ID.

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
  }
}

Google App Engine-Vorgang mit Autorisierungscode

Der Autorisierungscode-Vorgang in App Engine ist fast identisch mit dem Servlet. mit dem Autorisierungscode, außer dass wir die Users Java API Der Nutzer muss angemeldet sein, damit das Java-API für Nutzer aktiviert werden kann. für Informationen zur Weiterleitung von Nutzern auf eine Anmeldeseite, sofern diese angemeldet sind, siehe Sicherheit und Authentifizierung (in web.xml).

Der Hauptunterschied zum Servlet-Fall besteht darin, dass Sie konkrete abgeleiteten Klassen von AbstractAppEngineAuthorizationCodeServlet und AbstractAppEngineAuthorizationCodeCallbackServlet (von google-oauth-client-appengine). Sie erweitern die abstrakten Servlet-Klassen und implementieren die getUserId-Methode mithilfe der Users Java API für Sie. AppEngineDataStoreFactory (aus der Google HTTP-Clientbibliothek für Java) ist eine gute Option, um die Anmeldedaten mithilfe der Google App Engine Data Store API dauerhaft 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 von 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 Clientfluss

Dies sind die typischen Schritte des browserbasierten Clientflusses, der in der Implicit Grant-Spezifikation:

  • Verwenden Sie BrowserClientRequestUrl, den Browser des Endnutzers auf die Autorisierungsseite weiterleiten, Ihrer Anwendung Zugriff auf ihre geschützten Daten zu gewähren.
  • Verwenden Sie eine JavaScript-Anwendung, um das in der URL gefundene Zugriffstoken zu verarbeiten. Fragment beim Weiterleitungs-URI, der beim Autorisierungsserver registriert ist.

Verwendungsbeispiel für eine 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-Inhaberspezifikation Der Server wird aufgerufen, um auf eine geschützte Ressource mit einem abgelaufenen Zugriff zuzugreifen. -Token, antwortet der Server normalerweise mit dem HTTP-Statuscode 401 Unauthorized wie zum Beispiel:

   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. Für finden Sie in der Dokumentation des OAuth 2.0-Anbieters.

Alternativ können Sie den Parameter expires_in im Zugriffstoken-Antwort. Gibt die Lebensdauer des gewährten Zugriffstokens in Sekunden an: in der Regel eine Stunde. Das Zugriffstoken läuft jedoch möglicherweise nicht am Ende ab. und der Server lässt den Zugriff möglicherweise weiterhin zu. Aus diesem Grund empfehlen in der Regel, auf den Statuscode 401 Unauthorized zu warten, anstatt davon aus, dass das Token basierend auf der verstrichenen Zeit abgelaufen ist. Alternativ können Sie versuchen, ein Zugriffstoken kurz vor Ablauf zu aktualisieren, und wenn der Tokenserver nicht verfügbar ist, verwenden Sie das Zugriffstoken so lange, bis Sie eine 401 erhalten. Dieses ist die Strategie, die standardmäßig in Anmeldedaten:

Eine weitere Möglichkeit besteht darin, vor jeder Anfrage ein neues Zugriffstoken zu erhalten, erfordert jedes Mal eine zusätzliche HTTP-Anforderung an den Tokenserver. Es handelt sich daher wahrscheinlich schlechte Wahl in Bezug auf Geschwindigkeit und Netzwerknutzung. Idealerweise sollten Sie das Zugriffstoken in einem sicheren, nichtflüchtigen Speicher, um die Anfragen einer Anwendung nach neuem Zugriff zu minimieren Tokens. Bei installierten Anwendungen stellt der sichere Speicher jedoch eine Herausforderung dar.

Beachten Sie, dass ein Zugriffstoken aus anderen Gründen ungültig werden kann, wenn der Nutzer das Token explizit widerrufen hat, achten Sie also darauf, dass Ihr Fehlerbehandlungscode zuverlässig ist. Wenn Sie feststellen, dass ein Token nicht mehr gültig ist, wenn er beispielsweise abgelaufen ist oder widerrufen wurde, müssen Sie den Zugriff aus Ihrem Speicher. Auf Android-Geräten müssen Sie zum Beispiel AccountManager.invalidateAuthToken auf.