Utilizzo di OAuth 2.0 con la libreria client delle API di Google per Java

Panoramica

Scopo: il presente documento spiega come utilizzare i GoogleCredential per eseguire l'autorizzazione OAuth 2.0 con i servizi Google. Per per informazioni sulle funzioni generiche di OAuth 2.0 fornite, consulta OAuth 2.0 e libreria client OAuth di Google per Java.

Riepilogo:per accedere ai dati protetti archiviati sui servizi Google, usa OAuth 2.0 per l'autorizzazione. Le API di Google supportano i flussi OAuth 2.0 per diversi tipi di applicazioni client. In tutti questi flussi, l'applicazione client richiede un token di accesso associati solo alla tua applicazione client e al proprietario dei dati protetti a cui l'utente accede. Il token di accesso è associato anche a un ambito limitato che definisce il tipo di dati a cui ha accesso l'applicazione client (ad esempio "Gestisci le tue attività"). Un obiettivo importante di OAuth 2.0 è garantire un comodo accesso ai dati protetti, riducendo al minimo il potenziale impatto in caso di furto di un token di accesso.

I pacchetti OAuth 2.0 nella libreria client dell'API di Google per Java sono basati su per uso generico Libreria client OAuth 2.0 di Google per Java.

Per maggiori dettagli, consulta la documentazione Javadoc per i seguenti pacchetti:

Console API di Google

Prima di poter accedere alle API di Google, devi configurare un progetto nella Console API di Google per autenticazione e fatturazione il client sia un'applicazione installata, un'applicazione mobile, un server web o un client eseguito nel browser.

Per istruzioni su come configurare correttamente le credenziali, consulta le Guida della Console API.

Credenziale

GoogleCredential

GoogleCredential è una classe di supporto thread-safe per OAuth 2.0 che consente di accedere alle risorse protette usando un token di accesso. Ad esempio, se disponi già di un token di accesso, può effettuare una richiesta nel seguente modo:

GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Plus plus = new Plus.builder(new NetHttpTransport(),
                             GsonFactory.getDefaultInstance(),
                             credential)
    .setApplicationName("Google-PlusSample/1.0")
    .build();

Identità Google App Engine

Questa credenziale alternativa si basa sul API Java App Engine App Engine. A differenza della credenziale con cui un'applicazione client richiede l'accesso a un i dati dell'utente finale, l'API App Identity fornisce l'accesso al client dell'applicazione.

Usa AppIdentityCredential (da google-api-client-appengine). Questa credenziale è molto più semplice perché Google App Engine si occupa di tutte i dettagli. Devi specificare solo l'ambito OAuth 2.0 di cui hai bisogno.

Esempio di codice tratto da 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();
}

Datastore

Generalmente un token di accesso ha una data di scadenza di un'ora, dopodiché dovrai ricevi un messaggio di errore se provi a utilizzarlo. GoogleCredential si occupa di "aggiornare" automaticamente il token, il che significa semplicemente un nuovo token di accesso. Ciò viene fatto per mezzo di un token di aggiornamento di lunga durata, che viene generalmente ricevuto insieme al token di accesso se utilizzi Parametro access_type=offline durante il flusso del codice di autorizzazione (vedi GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).

La maggior parte delle applicazioni dovrà mantenere il token di accesso della credenziale e/o token di aggiornamento. Per mantenere i token di accesso e/o di aggiornamento della credenziale, puoi: Fornire la tua implementazione di DataStoreFactory con StoredCredential; In alternativa, puoi utilizzare una delle seguenti implementazioni fornite dalla libreria:

Utenti AppEngine: AppEngineCredentialStore è deprecato e verrà rimosso a breve. Ti consigliamo di utilizzare AppEngineDataStoreFactory con StoredCredential. Se le credenziali sono archiviate alla vecchia maniera, puoi utilizzare metodi helper migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) per eseguire la migrazione.

Puoi utilizzare DataStoreCredentialRefreshListener e impostala per la credenziale utilizzando GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).

Flusso del codice di autorizzazione

Utilizza il flusso del codice di autorizzazione per consentire all'utente finale di concedere la tua applicazione ai propri dati protetti nelle API di Google. Il protocollo per questo flusso è specificato in Concessione del codice di autorizzazione.

Questo flusso è implementato utilizzando GoogleAuthorizationCodeFlow. I passaggi sono:

In alternativa, se non utilizzi GoogleAuthorizationCodeFlow, puoi utilizzare le classi di livello inferiore:

Quando configuri il tuo progetto nella console API di Google, che selezioni tra le diverse credenziali, a seconda del flusso che stai utilizzando. Per maggiori dettagli, consulta l'articolo Configurare OAuth 2.0. e scenari OAuth 2.0. Di seguito sono riportati snippet di codice per ogni flusso.

Applicazioni server web

Il protocollo per questo flusso è spiegato in Utilizzo di OAuth 2.0 per applicazioni server web.

Questa libreria fornisce classi helper servlet per semplificare significativamente del codice di autorizzazione per casi d'uso di base. Devi solo fornire sottoclassi concrete di AbstractAuthorizationCodeServlet e AbstractAuthorizationCodeCallbackServlet (da google-oauth-client-servlet) e aggiungerli al file web.xml. Tieni presente che devi ancora occuparti del cliente per la tua applicazione web ed estrarre un ID utente.

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

Applicazioni di Google App Engine

Il flusso del codice di autorizzazione su App Engine è quasi identico al servlet del codice di autorizzazione, con la differenza che possiamo sfruttare API Users Java. L'utente deve aver effettuato l'accesso per abilitare l'API Users Java. per informazioni su reindirizzare gli utenti a una pagina di accesso nel caso in cui non abbiano già eseguito l'accesso, vedi Sicurezza e autenticazione (in web.xml).

La differenza principale rispetto al caso servlet è che fornisci informazioni delle sottoclassi AbstractAppEngineAuthorizationCodeServlet e AbstractAppEngineAuthorizationCodeCallbackServlet (da google-oauth-client-appengine. Estendono le classi servlet astratte e implementano il metodo getUserId utilizzando l'API Users Java. AppEngineDataStoreFactory (da google-http-client-appengine) è una buona opzione per mantenere la credenziale in modo permanente utilizzando i dati di Google App Engine API Store.

Esempio preso (leggermente modificato) da 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();
  }
}

Per un esempio aggiuntivo, vedi storage-serviceaccount-appengine-sample.

Account di servizio

GoogleCredential Supporta anche gli account di servizio. A differenza della credenziale con cui un'applicazione client richiede l'accesso a un i dati dell'utente finale, gli account di servizio forniscono l'accesso ai dati dati personali. L'applicazione client firma la richiesta di un token di accesso utilizzando Una chiave privata scaricata dalla console API di Google.

Codice di esempio preso da 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();
...

Per un esempio aggiuntivo, vedi storage-serviceaccount-cmdline-sample.

Furto d'identità

Puoi anche utilizzare il flusso dell'account di servizio per impersonare un utente in un dominio che che possiedi. È molto simile al flusso degli account di servizio qui sopra, ma inoltre chiama GoogleCredential.Builder.setServiceAccountUser(String).

Applicazioni installate

Si tratta del flusso del codice di autorizzazione dalla riga di comando descritto in Utilizzo di OAuth 2.0 per le applicazioni installate.

Snippet di esempio da 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");
}

Applicazioni lato client

Per utilizzare il flusso client basato su browser descritto in Con OAuth 2.0 per le applicazioni lato client, devi generalmente seguire questa procedura:

  1. Reindirizza l'utente finale nel browser alla pagina di autorizzazione utilizzando GoogleBrowserClientRequestUrl per concedere alla tua applicazione del browser l'accesso ai dati protetti dell'utente finale.
  2. Utilizza la libreria client dell'API di Google per JavaScript. per elaborare il token di accesso trovato nel frammento dell'URL all'URI di reindirizzamento registrati nella console API di Google.

Utilizzo di esempio per un'applicazione web:

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

Quale libreria utilizzare con Android:

Se stai sviluppando per Android e l'API di Google che vuoi utilizzare è inclusa nel libreria di Google Play Services, usala per ottenere prestazioni ed esperienza ottimali. Se l'API di Google che vuoi utilizzare con Android non fa parte della libreria Google Play Services, puoi utilizzare la libreria client dell'API di Google per Java, che supporta Android 4.0 (Ice Cream Sandwich) (o superiore) e come descritto qui. Il supporto per Android nel Google La libreria client dell'API per Java è @Beta.

Informazioni di base:

A partire da Eclair (SDK 2.1), gli account utente vengono gestiti su un dispositivo Android utilizzando l'account manager. L'autorizzazione di tutte le applicazioni Android è centralizzata gestite dall'SDK AccountManager Tu specifichi l'ambito OAuth 2.0 richiesto dalla tua applicazione, che restituirà un accesso da utilizzare.

L'ambito OAuth 2.0 viene specificato tramite il parametro authTokenType come oauth2: più l'ambito. Ad esempio:

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

Specifica l'accesso in lettura/scrittura all'API Google Tasks. Se hai bisogno di più Ambiti OAuth 2.0, utilizza un elenco separato da spazi.

Alcune API hanno parametri authTokenType speciali che funzionano. Ad esempio: "Gestisci le tue attività" è un alias per l'esempio authtokenType mostrato sopra.

Devi inoltre specificare la chiave API nel Console API di Google. In caso contrario, il token che ti viene fornito dall'AccountManager ti fornisce quota anonima, che di solito è molto bassa. Al contrario, specificando un'API chiave ricevi una quota senza costi più alta e, se vuoi, puoi configurare la fatturazione per l'utilizzo al di sopra di questo.

Esempio di snippet di codice tratto da 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;
  }
}