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

Überblick

Zweck:In diesem Dokument wird erläutert, wie die Dienstprogrammklasse GoogleCredential für die OAuth 2.0-Autorisierung mit Google-Diensten verwendet wird. Informationen zu den von uns bereitgestellten generischen OAuth 2.0-Funktionen finden Sie unter OAuth 2.0 und die Google OAuth-Clientbibliothek für Java.

Zusammenfassung:Verwenden Sie OAuth 2.0 zur Autorisierung, um auf geschützte Daten zuzugreifen, die in Google-Diensten gespeichert sind. Google APIs unterstützen OAuth 2.0-Abläufe für verschiedene Arten von Clientanwendungen. Bei 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 außerdem einem begrenzten Bereich zugeordnet, durch den die Art von Daten definiert wird, auf die Ihre Clientanwendung Zugriff hat (z. B. „Aufgaben verwalten“). Ein wichtiges Ziel von OAuth 2.0 ist es, einen sicheren und bequemen Zugriff auf die geschützten Daten zu ermöglichen und gleichzeitig die möglichen Auswirkungen des Diebstahls eines Zugriffstokens zu minimieren.

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

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

Google API Console

Bevor Sie auf Google APIs zugreifen können, müssen Sie zu Authentifizierungs- und Abrechnungszwecken in der Google API Console ein Projekt einrichten, unabhängig davon, ob Ihr Client eine installierte Anwendung, eine mobile Anwendung, ein Webserver oder ein im Browser ausgeführter Client ist.

Eine Anleitung zur korrekten Einrichtung Ihrer Anmeldedaten finden Sie in der Hilfe zur API-Konsole.

Anmeldedaten

GoogleCredential

GoogleCredential ist eine Thread-sichere Hilfsklasse für OAuth 2.0 für den Zugriff auf geschützte Ressourcen mithilfe eines Zugriffstokens. Wenn Sie beispielsweise bereits ein Zugriffstoken haben, können Sie so eine Anfrage 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 alternativen 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 eigenen Daten der Clientanwendung.

Verwenden Sie AppIdentityCredential (von google-api-client-appengine). Diese Anmeldedaten sind viel einfacher, da sich Google App Engine um alle Details kümmert. Sie geben nur den erforderlichen OAuth 2.0-Bereich an.

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

Zugriffstokens laufen normalerweise nach einer Stunde ab. Danach erhalten Sie eine Fehlermeldung, wenn Sie versuchen, das Token zu verwenden. GoogleCredential "aktualisiert" das Token automatisch, was einfach bedeutet, dass ein neues Zugriffstoken abgerufen wird. Dies erfolgt mithilfe eines langlebigen Aktualisierungstokens, das normalerweise zusammen mit dem Zugriffstoken empfangen wird, wenn Sie den Parameter access_type=offline während des Autorisierungscode-Vorgangs verwenden (siehe GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).

Die meisten Anwendungen müssen das Zugriffstoken und/oder das Aktualisierungstoken der Anmeldedaten dauerhaft speichern. Wenn Sie die Zugriffs- und/oder Aktualisierungstokens für die Anmeldedaten beibehalten möchten, können Sie Ihre eigene Implementierung von DataStoreFactory mit StoredCredential bereitstellen oder eine der folgenden Implementierungen der Bibliothek verwenden:

AppEngine-Nutzer:AppEngineCredentialStore wurde verworfen und wird bald entfernt. Wir empfehlen die Verwendung von AppEngineDataStoreFactory mit StoredCredential. Wenn Sie Anmeldedaten auf die alte Art gespeichert haben, können Sie die hinzugefügten Hilfsmethoden migrateTo(AppEngineDataStoreFactory) oder migrateTo(DataStore) für die Migration verwenden.

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

Vorgang mit Autorisierungscode

Verwenden Sie den Ablauf mit Autorisierungscode, 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 Autorisierungscode-Gewährung angegeben.

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

Falls Sie GoogleAuthorizationCodeFlow nicht nutzen, können Sie alternativ die niedrigeren Klassen verwenden:

Wenn Sie Ihr Projekt in der Google API Console einrichten, können Sie je nach verwendetem Ablauf zwischen verschiedenen Anmeldedaten auswählen. Weitere Informationen finden Sie unter OAuth 2.0 einrichten und OAuth 2.0-Szenarien. Code-Snippets für jeden der Abläufe sind unten aufgeführt.

Webserveranwendungen

Das Protokoll für diesen Vorgang wird unter OAuth 2.0 für Webserveranwendungen verwenden erläutert.

Diese Bibliothek bietet Servlet-Hilfsklassen, um den Vorgang mit Autorisierungscode für grundlegende Anwendungsfälle erheblich zu vereinfachen. Sie stellen einfach konkrete abgeleitete Klassen von AbstractAuthorizationCodeServlet und AbstractAuthorizationCodeCallbackServlet (von google-oauth-client-servlet) bereit und fügen sie der Datei web.xml hinzu. Beachten Sie, dass Sie sich weiterhin um die Nutzeranmeldung für Ihre Webanwendung kümmern und eine Nutzer-ID extrahieren müssen.

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 Vorgang mit dem Autorisierungscode in App Engine ist fast identisch mit dem Ablauf mit dem Servlet-Autorisierungscode, mit der Ausnahme, dass wir die Users Java API von Google App Engine nutzen können. Damit die Users Java API aktiviert werden kann, muss der Nutzer angemeldet sein. Informationen zum Weiterleiten von Nutzern auf eine 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 (von google-oauth-client-appengine) angeben. Sie erweitern die abstrakten Servlet-Klassen und implementieren die Methode getUserId mithilfe der Users Java API. AppEngineDataStoreFactory (von google-http-client-appengine) ist eine gute Option, um die Anmeldedaten mit der Google App Engine Data Store API zu speichern.

Leicht geändertes Beispiel aus 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();
  }
}

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

Dienstkonten

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

Beispielcode aus plus-serviceaccount-cmdline-sample:

HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
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 Dienstkontoablauf auch verwenden, um die Identität eines Nutzers in einer Domain zu übernehmen, deren Inhaber Sie sind. Dies ist dem oben beschriebenen Ablauf für Dienstkonten sehr ähnlich, aber Sie rufen zusätzlich GoogleCredential.Builder.setServiceAccountUser(String) auf.

Installierte Apps

Dies ist der unter OAuth 2.0 für installierte Anwendungen beschriebene Ablauf mit Autorisierungscode über die Befehlszeile.

Beispiel-Snippet aus plus-cmdline-sample:

public static void main(String[] args) {
  try {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    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 unter OAuth 2.0 für clientseitige Anwendungen verwenden beschriebenen browserbasierten Clientvorgang verwenden möchten, gehen Sie in der Regel so vor:

  1. Leiten Sie den Endnutzer im Browser mithilfe von GoogleBrowserClientRequestUrl zur Autorisierungsseite weiter, 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 des Weiterleitungs-URI gespeichert ist, der in der Google API Console registriert ist.

Verwendungsbeispiel für eine 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 kann ich mit Android verwenden?

Wenn Sie für Android entwickeln und die Google API, die Sie verwenden möchten, in der Bibliothek der Google Play-Dienste enthalten ist, verwenden Sie diese Bibliothek, um die beste Leistung und Nutzererfahrung zu erzielen. Wenn die Google API, die Sie mit Android verwenden möchten, nicht Teil der Bibliothek der Google Play-Dienste 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 gesamte Autorisierung von Android-Anwendungen wird zentral vom SDK mithilfe von AccountManager verwaltet. Sie geben den OAuth 2.0-Bereich Ihrer Anwendung an und es wird ein zu verwendendes Zugriffstoken zurückgegeben.

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

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

Damit wird der Lese-/Schreibzugriff auf die Google Tasks API festgelegt. 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. Zum Beispiel ist „Aufgaben verwalten“ ein Alias für das oben gezeigte authtokenType-Beispiel.

Außerdem müssen Sie den API-Schlüssel in der Google API Console angeben. Andernfalls stellt Ihnen das Token, das Sie vom Account Manager erhalten, nur ein anonymes Kontingent zur Verfügung, was in der Regel sehr niedrig ist. Wenn Sie dagegen einen API-Schlüssel angeben, erhalten Sie ein höheres kostenloses Kontingent. Sie können optional die Abrechnung für die darüber liegende Nutzung einrichten.

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