OAuth 2.0 mit der Google API-Clientbibliothek für Java verwenden

Übersicht

Zweck:In diesem Dokument wird erläutert, wie Sie die Utility-Klasse GoogleCredential verwenden, um die OAuth 2.0-Autorisierung mit Google-Diensten durchzuführen. Informationen zu den generischen OAuth 2.0-Funktionen, die wir bereitstellen, finden Sie unter OAuth 2.0 und die Google OAuth-Clientbibliothek für Java.

Zusammenfassung:Wenn Sie auf geschützte Daten zugreifen möchten, die in Google-Diensten gespeichert sind, verwenden Sie OAuth 2.0 zur Autorisierung. Google APIs unterstützen OAuth 2.0-Abläufe für verschiedene Arten von Clientanwendungen. In allen diesen Abläufen fordert die Clientanwendung ein Zugriffstoken an, das nur mit Ihrer Clientanwendung und dem Inhaber der geschützten Daten verknüpft ist, auf die zugegriffen wird. Das Zugriffstoken ist auch mit einem eingeschränkten Bereich verknüpft, der die Art der Daten definiert, auf die Ihre Clientanwendung Zugriff hat (z. B. „Aufgaben verwalten“). Ein wichtiges Ziel von OAuth 2.0 ist es, einen sicheren und komfortablen Zugriff auf die geschützten Daten zu ermöglichen und gleichzeitig die potenziellen Auswirkungen zu minimieren, wenn ein Zugriffstoken gestohlen wird.

Die OAuth 2.0-Pakete in der Google API-Clientbibliothek für Java basieren auf der universellen Google OAuth 2.0-Clientbibliothek für Java.

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

Google API-Konsole

Bevor Sie auf Google-APIs zugreifen können, müssen Sie in der Google API Console ein Projekt für die Authentifizierung und Abrechnung einrichten. Das gilt unabhängig davon, ob Ihr Client eine installierte Anwendung, eine mobile Anwendung, ein Webserver oder ein Client ist, der im Browser ausgeführt wird.

Eine Anleitung zum Einrichten Ihrer Anmeldedaten finden Sie in der Hilfe zur API Console.

Anmeldedaten

GoogleCredential

GoogleCredential ist eine threadsichere Hilfsklasse für OAuth 2.0 für den Zugriff auf geschützte Ressourcen mit einem Zugriffstoken. Wenn Sie beispielsweise bereits ein Zugriffstoken haben, können Sie eine Anfrage so stellen:

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-Identität

Diese alternative Anmeldedaten basieren auf der Google App Engine App Identity Java API. Im Gegensatz zu Anmeldedaten, bei denen eine Clientanwendung Zugriff auf die Daten eines Endnutzers anfordert, bietet die App Identity API Zugriff auf die Daten der Clientanwendung selbst.

Verwenden Sie AppIdentityCredential (aus google-api-client-appengine). Dieses Anmeldedatenobjekt ist viel einfacher, da Google App Engine sich um alle Details kümmert. Sie geben nur den OAuth 2.0-Bereich an, den Sie benötigen.

Beispielcode aus 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();
}

Datenspeicher

Ein Zugriffstoken läuft normalerweise nach einer Stunde ab. Danach wird bei Verwendung des Tokens ein Fehler zurückgegeben. Die Bibliothek GoogleCredential "aktualisiert" das Token automatisch, was einfach bedeutet, dass ein neues Zugriffstoken abgerufen wird. Dies erfolgt mithilfe eines langlebigen Aktualisierungstokens, das in der Regel zusammen mit dem Zugriffstoken empfangen wird, wenn Sie den Parameter access_type=offline während des Autorisierungscode-Ablaufs verwenden (siehe GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).

In den meisten Anwendungen muss das Zugriffstoken und/oder das Aktualisierungstoken der Anmeldedaten gespeichert werden. Wenn Sie die Zugriffs- und/oder Aktualisierungstokens der Anmeldedaten beibehalten möchten, können Sie Ihre eigene Implementierung von DataStoreFactory mit StoredCredential bereitstellen oder eine der folgenden Implementierungen verwenden, die von der Bibliothek bereitgestellt werden:

  • AppEngineDataStoreFactory: Die Anmeldedaten werden mit der Google App Engine Data Store API gespeichert.
  • MemoryDataStoreFactory: Die Anmeldedaten werden im Arbeitsspeicher „gespeichert“, was nur als kurzfristiger Speicher für die Lebensdauer des Prozesses sinnvoll ist.
  • FileDataStoreFactory: Die Anmeldedaten werden in einer Datei gespeichert.

AppEngine-Nutzer:AppEngineCredentialStore wurde eingestellt und wird bald 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 die Migration durchzuführen.

Sie können DataStoreCredentialRefreshListener verwenden und für die Anmeldedaten mit GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener) festlegen.

Autorisierungscode-Ablauf

Verwenden Sie den Autorisierungscode-Ablauf, damit der Endnutzer Ihrer Anwendung Zugriff auf seine geschützten Daten in Google APIs gewähren kann. Das Protokoll für diesen Ablauf wird unter Grant-Typ „Autorisierungscode“ beschrieben.

Dieser Vorgang wird mit GoogleAuthorizationCodeFlow implementiert. Folgende Schritte sind auszuführen:

  • Der Endnutzer meldet sich in Ihrer Anwendung an. Sie müssen diesen Nutzer mit einer Nutzer-ID verknüpfen, die für Ihre Anwendung eindeutig ist.
  • Rufen Sie AuthorizationCodeFlow.loadCredential(String) basierend auf der Nutzer-ID auf, um zu prüfen, ob die Anmeldedaten des Endnutzers bereits bekannt sind. Falls ja, sind Sie fertig.
  • Rufen Sie andernfalls AuthorizationCodeFlow.newAuthorizationUrl() auf und leiten Sie den Browser des Endnutzers zu einer Autorisierungsseite weiter, damit Ihre Anwendung Zugriff auf seine geschützten Daten erhält.
  • Der Google-Autorisierungsserver leitet den Browser dann zusammen mit einem code-Suchparameter zurück an die von Ihrer Anwendung angegebene Weiterleitungs-URL weiter. Verwenden Sie den Parameter code, um ein Zugriffstoken mit AuthorizationCodeFlow.newTokenRequest(String) anzufordern.
  • Verwenden Sie AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String)), um Anmeldedaten für den Zugriff auf geschützte Ressourcen zu speichern und abzurufen.

Wenn Sie GoogleAuthorizationCodeFlow nicht verwenden, können Sie alternativ die Klassen auf niedrigerer Ebene verwenden:

Wenn Sie Ihr Projekt in der Google API Console einrichten, wählen Sie je nach verwendetem Ablauf verschiedene Anmeldedaten aus. Weitere Informationen finden Sie unter OAuth 2.0 einrichten und OAuth 2.0-Szenarien. Code-Snippets für die einzelnen Abläufe finden Sie unten.

Webserveranwendungen

Das Protokoll für diesen Ablauf wird unter OAuth 2.0 für Webserveranwendungen verwenden beschrieben.

Diese Bibliothek enthält Servlet-Helper-Klassen, die den Autorisierungscode-Ablauf für grundlegende Anwendungsfälle erheblich vereinfachen. Sie müssen nur konkrete Unterklassen von AbstractAuthorizationCodeServlet und AbstractAuthorizationCodeCallbackServlet (aus google-oauth-client-servlet) bereitstellen und sie der Datei web.xml hinzufügen. Sie müssen sich weiterhin um die Nutzeranmeldung für Ihre Webanwendung kümmern und eine Nutzer-ID extrahieren.

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

Der Autorisierungscode-Ablauf in App Engine ist fast identisch mit dem Servlet-Autorisierungscode-Ablauf. Der einzige Unterschied besteht darin, dass wir die Users Java API von Google App Engine nutzen können. Der Nutzer muss angemeldet sein, damit die Users Java API aktiviert wird. Informationen zum Weiterleiten von Nutzern auf eine Anmeldeseite, wenn sie noch nicht angemeldet sind, finden Sie unter Security and Authentication (in web.xml).

Der Hauptunterschied zum Servlet-Fall besteht darin, dass Sie konkrete Unterklassen von AbstractAppEngineAuthorizationCodeServlet und AbstractAppEngineAuthorizationCodeCallbackServlet (aus google-oauth-client-appengine) bereitstellen. Sie erweitern die abstrakten Servlet-Klassen und implementieren die getUserId-Methode für Sie mit der Users Java API. AppEngineDataStoreFactory (aus google-http-client-appengine) ist eine gute Option zum Speichern der Anmeldedaten mit der Google App Engine Data Store API.

Beispiel aus calendar-appengine-sample (leicht geändert):

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();
  }
}

Ein weiteres Beispiel finden Sie unter storage-serviceaccount-appengine-sample.

Dienstkonten

GoogleCredential unterstützt auch Dienstkonten. Im Gegensatz zu den Anmeldedaten, mit denen eine Clientanwendung Zugriff auf die Daten eines Endnutzers anfordert, ermöglichen Dienstkonten den Zugriff auf die Daten der Clientanwendung selbst. Ihre Clientanwendung signiert die Anfrage für ein Zugriffstoken mit einem privaten Schlüssel, der aus der Google API Console heruntergeladen wurde.

Nutzungsbeispiel:

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();
...

Ein weiteres Beispiel finden Sie unter storage-serviceaccount-cmdline-sample.

Identitätsdiebstahl

Sie können den Dienstkonto-Ablauf auch verwenden, um die Identität eines Nutzers in einer Domain zu übernehmen, die Ihnen gehört. Dieser Ablauf ähnelt dem Dienstkontoablauf oben, aber Sie rufen zusätzlich GoogleCredential.Builder.setServiceAccountUser(String) auf.

Installierte Apps

Dies ist der Befehlszeilen-Vorgang mit Autorisierungscode, der unter OAuth 2.0 für installierte Anwendungen verwenden beschrieben wird.

Nutzungsbeispiel:

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");
}

Clientseitige Anwendungen

Wenn Sie den browserbasierten Client-Flow verwenden möchten, der unter OAuth 2.0 für clientseitige Anwendungen beschrieben wird, gehen Sie in der Regel so vor:

  1. Leiten Sie den Endnutzer im Browser zur Autorisierungsseite weiter, indem Sie GoogleBrowserClientRequestUrl verwenden, um Ihrer Browseranwendung Zugriff auf die geschützten Daten des Endnutzers zu gewähren.
  2. Verwenden Sie die Google API-Clientbibliothek für JavaScript, um das Zugriffstoken zu verarbeiten, das im URL-Fragment der Umleitungs-URI enthalten ist, die in der Google API Console registriert ist.

Beispiel für die Verwendung in einer Webanwendung:

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

@Beta

Welche Bibliothek für Android verwendet werden sollte:

Wenn Sie für Android entwickeln und die Google API, die Sie verwenden möchten, in der Google Play Services-Bibliothek enthalten ist, sollten Sie diese Bibliothek verwenden, um die beste Leistung und Nutzerfreundlichkeit zu erzielen. Wenn die Google API, die Sie mit Android verwenden möchten, nicht Teil der Google Play-Dienste-Bibliothek ist, können Sie die Google API-Clientbibliothek für Java verwenden, die Android 4.0 (Ice Cream Sandwich) oder höher unterstützt und hier beschrieben wird. Die Unterstützung für Android in der Google API-Clientbibliothek für Java ist @Beta.

Hintergrund:

Ab Eclair (SDK 2.1) werden Nutzerkonten auf einem Android-Gerät mit dem Account Manager verwaltet. Die Autorisierung aller Android-Anwendungen wird zentral vom SDK über AccountManager verwaltet. Sie geben den OAuth 2.0-Bereich an, den Ihre Anwendung benötigt, und erhalten ein Zugriffstoken zurück, das Sie verwenden können.

Der OAuth 2.0-Bereich wird über den Parameter authTokenType als oauth2: plus dem Bereich angegeben. Beispiel:

oauth2:https://www.googleapis.com/auth/tasks

Damit wird Lese-/Schreibzugriff auf die Google Tasks API gewährt. Wenn Sie mehrere OAuth 2.0-Bereiche benötigen, verwenden Sie eine durch Leerzeichen getrennte Liste.

Einige APIs haben spezielle authTokenType-Parameter, die ebenfalls funktionieren. „Manage your tasks“ ist beispielsweise ein Alias für das oben gezeigte Beispiel authtokenType.

Außerdem müssen Sie den API-Schlüssel aus der Google API Console angeben. Andernfalls erhalten Sie vom AccountManager nur ein anonymes Kontingent, das in der Regel sehr niedrig ist. Wenn Sie einen API-Schlüssel angeben, erhalten Sie dagegen ein höheres kostenloses Kontingent und können optional die Abrechnung für die Nutzung darüber hinaus einrichten.

Beispiel für ein Code-Snippet aus 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;
  }
}