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

Übersicht

Zweck:In diesem Dokument wird erläutert, wie Sie die OAuth 2.0-Autorisierung mit Google-Diensten mithilfe der Dienstprogrammklasse GoogleCredential ausführen. 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 für die 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 all diesen Abläufen fordert die Clientanwendung ein Zugriffstoken an, das nur Ihrer Clientanwendung und dem Inhaber der geschützten Daten zugeordnet ist, auf die zugegriffen wird. Das Zugriffstoken ist auch mit einem begrenzten Bereich verknüpft, der die Art von Daten definiert, auf die Ihre Clientanwendung Zugriff hat (z. B. &Ihre Aufgaben verwalten"). Ein wichtiges Ziel für OAuth 2.0 ist es, einen sicheren und bequemen Zugriff auf die geschützten Daten zu ermöglichen und gleichzeitig die möglichen Auswirkungen zu minimieren, wenn ein Zugriffstoken gestohlen wird.

Die OAuth 2.0-Pakete in der Google API-Clientbibliothek für Java basieren auf der 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 in der Google API Console ein Projekt für Authentifizierungs- und Abrechnungszwecke einrichten. Dies gilt unabhängig davon, ob es sich bei Ihrem Client um eine installierte Anwendung, eine mobile Anwendung, einen Webserver oder einen Client im Browser handelt.

Eine Anleitung zum ordnungsgemäßen Einrichten deiner Anmeldedaten findest du in der API Console-Hilfe.

Anmeldedaten

GoogleCredential

GoogleCredential ist eine threadsichere 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 den Anmeldedaten, mit 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 (aus google-api-client-appengine). Diese Anmeldedaten sind wesentlich einfacher, da Google App Engine alle Details übernimmt. 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

Ein Zugriffstoken läuft in der Regel nach 1 Stunde ab. Danach erhalten Sie eine Fehlermeldung, wenn Sie es verwenden möchten. GoogleCredential aktualisiert das Token automatisch, d. h., es wird lediglich ein neues Zugriffstoken abgerufen. Dazu wird ein langlebiges Aktualisierungstoken verwendet, das normalerweise zusammen mit dem Zugriffstoken empfangen wird, wenn Sie den Parameter access_type=offline während des Autorisierungscode-Ablaufs verwenden (siehe GoogleAutorisierungCodeFlow.Builder.setAccessType(String)).

Die meisten Anwendungen müssen das Zugriffstoken und/oder das Aktualisierungstoken beibehalten. Sie können Ihre eigene Implementierung von DataStoreFactory mit StoredCredential bereitstellen oder eine der folgenden Implementierungen der Bibliothek verwenden, um den Zugriffs- und/oder Aktualisierungstoken der Anmeldedaten beizubehalten.

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

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

Ablauf des Autorisierungscodes

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 Vorgang wird in der Autorisierung von Codeberechtigungen beschrieben.

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

  • Der Endnutzer meldet sich in Ihrer Anwendung an. Sie müssen diesen Nutzer dann einer Nutzer-ID zuordnen, die für Ihre Anwendung eindeutig ist.
  • Rufen Sie anhand der User-ID AutorisierungCodeFlow.loadCredential(String) auf, um zu prüfen, ob die Anmeldedaten des Endnutzers bereits bekannt sind. In diesem Fall sind wir fertig.
  • Ist dies nicht der Fall, rufen Sie AutorisierungCodeFlow.newAutorisierungUrl() auf und leiten Sie den Browser des Endnutzers auf eine Autorisierungsseite weiter, um Ihrer Anwendung Zugriff auf ihre geschützten Daten zu gewähren.
  • Der Google-Autorisierungsserver leitet den Browser dann zurück an die von Ihrer Anwendung angegebene Weiterleitungs-URL zusammen mit einem code-Abfrageparameter. Verwenden Sie den Parameter code, um mit AutorisierungCodeFlow.newTokenRequest(String) ein Zugriffstoken anzufordern.
  • Verwenden Sie AutorisierungCodeFlow.createAndStoreCredential(TokenResponse, String)), um Anmeldedaten für den Zugriff auf geschützte Ressourcen zu speichern und abzurufen.

Wenn Sie GoogleAutorisierungCodeFlow nicht verwenden, können Sie auch die untergeordneten Klassen 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. Die Code-Snippets für die einzelnen Abläufe finden Sie unten.

Webserveranwendungen

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

Diese Bibliothek bietet Servlet-Hilfsklassen, die den Vorgang des Autorisierungscodes für grundlegende Anwendungsfälle erheblich vereinfachen. Sie stellen einfach konkrete Unterklassen von abstrakteAutorisierungCodeServlet und ZusammenfassungAutorisierungCodeCallbackServlet (von google-oauth-client-Servlet) bereit und fügen sie Ihrer web.xml-Datei hinzu. Sie müssen aber weiterhin die Nutzeranmeldung für Ihre Webanwendung vornehmen und eine User-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 nahezu identisch mit dem Ablauf des Servlet-Autorisierungscodes. 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 Java-API für Nutzer aktiviert wird. Informationen zum Weiterleiten von Nutzern auf eine Anmeldeseite, sofern sie noch nicht angemeldet sind, finden Sie unter Sicherheit und Authentifizierung (in web.xml).

Der Hauptunterschied zum Aggregatorfall besteht darin, dass Sie konkrete Unterklassen von abstrakteAppEngineAutorisierungCodeServlet und abstrakteAppEngineAutorisierungCodeCallbackServlet (von google-oauth-client-appengine) bereitstellen. Sie erweitern die abstrakten Servlet-Klassen und implementieren die Methode getUserId mithilfe der Users Java API für Sie. AppEngineDataStoreFactory (von google-http-client-appengine) ist eine gute Option, um die Anmeldedaten mithilfe der Google App Engine Data Store API zu speichern.

Beispiel (leicht geändert) von 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 zusätzliches 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, bieten Dienstkonten Zugriff auf die eigenen Daten der Clientanwendung. Ihre Clientanwendung signiert die Anfrage für ein Zugriffstoken mithilfe eines privaten Schlüssels, der aus der Google API Console heruntergeladen wurde.

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 zusätzliches Beispiel finden Sie unter storage-serviceaccount-cmdline-sample.

Identitätsdiebstahl

Sie können den Dienstkontofluss auch verwenden, um die Identität eines Nutzers in einer Domain anzunehmen, deren Inhaber Sie sind. Dieser Vorgang ist dem oben beschriebenen Dienstkonto sehr ähnlich, aber rufen Sie zusätzlich GoogleCredential.Builder.setServiceAccountUser(String) auf.

Installierte Apps

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

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 browserbasierten Clientfluss verwenden möchten, der unter OAuth 2.0 für clientseitige Anwendungen beschrieben wird, gehen Sie normalerweise 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. Verwende die Google API-Clientbibliothek für JavaScript, um das Zugriffstoken im URL-Fragment beim Weiterleitungs-URI zu verarbeiten, 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 Mediathek soll ich mit Android verwenden?

Wenn du die Entwicklung für Android entwickelst und die Google API, die du verwenden möchtest, in der Mediathek der Google Play-Dienste enthalten ist, solltest du diese Bibliothek verwenden, um die beste Leistung 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 die hier beschrieben ist. Die Android-Unterstützung in der Google API-Clientbibliothek für Java ist @Beta.

Hintergrund:

Ab Eclair (SDK 2.1) werden Nutzerkonten auf dem Android-Gerät über den Account Manager verwaltet. Die gesamte Android-Anwendungsautorisierung wird zentral vom SDK mit AccountManager verwaltet. Sie geben den von Ihrer Anwendung benötigten OAuth 2.0-Bereich an und er gibt ein Zugriffstoken zurück.

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

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

Gibt den Lese-/Schreibzugriff auf die Google Tasks API an. Wenn Sie mehrere OAuth 2.0-Umfänge benötigen, verwenden Sie eine durch Leerzeichen getrennte Liste.

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

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

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