Panoramica
Scopo:questo documento spiega come utilizzare la classe di utilità GoogleCredential per eseguire l'autorizzazione OAuth 2.0 con i servizi Google. Per informazioni sulle funzioni OAuth 2.0 generiche che forniamo, consulta OAuth 2.0 e la libreria client OAuth di Google per Java.
Riepilogo: per accedere ai dati protetti archiviati sui servizi Google, utilizza 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 associato solo all'applicazione client e al proprietario dei dati protetti a cui si accede. Il token di accesso è associato anche a un ambito limitato che definisce il tipo di dati a cui la tua applicazione client ha accesso (ad esempio "Gestisci le tue attività"). Un obiettivo importante di OAuth 2.0 è fornire un accesso sicuro e comodo ai dati protetti, riducendo al minimo l'impatto potenziale 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 sulla libreria client Google OAuth 2.0 per Java di uso generale.
Per maggiori dettagli, consulta la documentazione Javadoc per i seguenti pacchetti:
- com.google.api.client.googleapis.auth.oauth2 (da google-api-client)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (da google-api-client-appengine)
Console API di Google
Prima di poter accedere alle API di Google, devi configurare un progetto nella console API di Google per l'autenticazione e la fatturazione, indipendentemente dal fatto che il client sia un'applicazione installata, un'applicazione mobile, un server web o un client eseguito nel browser.
Per istruzioni sulla configurazione corretta delle credenziali, consulta la guida della console API.
Credenziale
GoogleCredential
GoogleCredential è una classe helper thread-safe per OAuth 2.0 per l'accesso a risorse protette utilizzando un token di accesso. Ad esempio, se hai già un token di accesso, puoi inviare 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 sull'API Java di Google App Engine App Identity. A differenza delle credenziali in cui un'applicazione client richiede l'accesso ai dati di un utente finale, l'API App Identity fornisce l'accesso ai dati dell'applicazione client.
Utilizza AppIdentityCredential (da google-api-client-appengine). Questa credenziale è molto più semplice perché Google App Engine si occupa di tutti i dettagli. Specifica 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
Un token di accesso in genere ha una data di scadenza di 1 ora, dopo la quale
riceverai un errore se provi a utilizzarlo.
GoogleCredential
si occupa di "aggiornare" automaticamente il token, il che significa semplicemente ottenere
un nuovo token di accesso. Ciò avviene tramite un token di aggiornamento di lunga durata, che
in genere viene ricevuto insieme al token di accesso se utilizzi il
parametro access_type=offline durante il flusso del codice di autorizzazione (vedi
GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).
La maggior parte delle applicazioni dovrà rendere persistente il token di accesso e/o il token di aggiornamento della credenziale. Per rendere persistenti i token di accesso e/o di aggiornamento delle credenziali, puoi fornire la tua implementazione di DataStoreFactory con StoredCredential; oppure puoi utilizzare una delle seguenti implementazioni fornite dalla libreria:
- AppEngineDataStoreFactory: persiste le credenziali utilizzando l'API Google App Engine Data Store.
- MemoryDataStoreFactory: "persiste" la credenziale in memoria, il che è utile solo come spazio di archiviazione a breve termine per la durata del processo.
- FileDataStoreFactory: salva le credenziali in un file.
Utenti App Engine: AppEngineCredentialStore è deprecato e verrà rimosso a breve. Ti consigliamo di utilizzare AppEngineDataStoreFactory con StoredCredential. Se hai credenziali archiviate nel vecchio modo, puoi utilizzare i metodi helper aggiunti migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) per eseguire la migrazione.
Puoi utilizzare DataStoreCredentialRefreshListener e impostarlo per le credenziali utilizzando GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Flusso del codice di autorizzazione
Utilizza il flusso del codice di autorizzazione per consentire all'utente finale di concedere alla tua applicazione l'accesso ai propri dati protetti sulle API di Google. Il protocollo per questo flusso è specificato in Autorizzazione tramite codice di autorizzazione.
Questo flusso viene implementato utilizzando GoogleAuthorizationCodeFlow. I passaggi sono:
- L'utente finale accede alla tua applicazione. Dovrai associare l'utente a un ID utente univoco per la tua applicazione.
- Chiama AuthorizationCodeFlow.loadCredential(String)) in base all'ID utente per verificare se le credenziali dell'utente finale sono già note. Se è così, abbiamo finito.
- In caso contrario, chiama AuthorizationCodeFlow.newAuthorizationUrl() e indirizza il browser dell'utente finale a una pagina di autorizzazione per concedere alla tua applicazione l'accesso ai suoi dati protetti.
- Il server di autorizzazione Google reindirizzerà il browser all'URL di reindirizzamento specificato dall'applicazione, insieme a un parametro di query
code. Utilizza il parametrocodeper richiedere un token di accesso utilizzando AuthorizationCodeFlow.newTokenRequest(String)). - Utilizza AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String)) per archiviare e ottenere una credenziale per accedere alle risorse protette.
In alternativa, se non utilizzi GoogleAuthorizationCodeFlow, puoi utilizzare le classi di livello inferiore:
- Utilizza DataStore.get(String) per caricare la credenziale dallo store in base all'ID utente.
- Utilizza GoogleAuthorizationCodeRequestUrl per indirizzare il browser alla pagina di autorizzazione.
- Utilizza AuthorizationCodeResponseUrl per elaborare la risposta di autorizzazione e analizzare il codice di autorizzazione.
- Utilizza GoogleAuthorizationCodeTokenRequest per richiedere un token di accesso ed eventualmente un token di aggiornamento.
- Crea un nuovo GoogleCredential e archivialo utilizzando DataStore.set(String, V).
- Accedi alle risorse protette utilizzando
GoogleCredential. I token di accesso scaduti verranno aggiornati automaticamente utilizzando il token di aggiornamento (se applicabile). Assicurati di utilizzare DataStoreCredentialRefreshListener e di impostarlo per le credenziali utilizzando GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Quando configuri il progetto nella console API di Google, selezioni tra diverse credenziali, a seconda del flusso che utilizzi. Per maggiori dettagli, consulta Configurare OAuth 2.0 e Scenari OAuth 2.0. Di seguito sono riportati gli snippet di codice per ciascun flusso.
Applicazioni server web
Il protocollo per questo flusso è spiegato nell'articolo sull'utilizzo di OAuth 2.0 per applicazioni server web.
Questa libreria fornisce classi helper servlet per semplificare notevolmente il flusso del codice di autorizzazione per i casi d'uso di base. Devi solo fornire sottoclassi concrete di AbstractAuthorizationCodeServlet e AbstractAuthorizationCodeCallbackServlet (da google-oauth-client-servlet) e aggiungerle al file web.xml. Tieni presente che devi comunque occuparti dell'accesso degli utenti 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 Google App Engine
Il flusso del codice di autorizzazione su App Engine è quasi identico al flusso del codice di autorizzazione servlet, tranne per il fatto che possiamo sfruttare l'API Java Users di Google App Engine. L'utente deve aver eseguito l'accesso per attivare l'API Java Users. Per informazioni su come reindirizzare gli utenti a una pagina di accesso se non hanno ancora eseguito l'accesso, consulta Sicurezza e autenticazione (in web.xml).
La differenza principale rispetto al caso del servlet è che fornisci sottoclassi concrete di
AbstractAppEngineAuthorizationCodeServlet e AbstractAppEngineAuthorizationCodeCallbackServlet
(da google-oauth-client-appengine).
Estendono le classi servlet astratte e implementano il metodo getUserId
per te utilizzando l'API Users Java. AppEngineDataStoreFactory
(da google-http-client-appengine)
è una buona opzione per rendere persistenti le credenziali utilizzando l'API Google App Engine Data
Store.
Esempio tratto (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 altro esempio, vedi storage-serviceaccount-appengine-sample.
Service account
GoogleCredential supporta anche gli account di servizio. A differenza delle credenziali in cui un'applicazione client richiede l'accesso ai dati di un utente finale, gli account di servizio forniscono l'accesso ai dati dell'applicazione client. L'applicazione client firma la richiesta di un token di accesso utilizzando una chiave privata scaricata dalla console API di Google.
Esempio di utilizzo:
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(); ...
Per un altro esempio, vedi storage-serviceaccount-cmdline-sample.
Furto d'identità
Puoi anche utilizzare il flusso dell'account di servizio per rappresentare un utente in un dominio di tua proprietà. Questo flusso è molto simile a quello dell'account di servizio riportato sopra, ma in più chiami GoogleCredential.Builder.setServiceAccountUser(String).
Applicazioni installate
Si tratta del flusso del codice di autorizzazione della riga di comando descritto in Utilizzo di OAuth 2.0 per le applicazioni installate.
Esempio di utilizzo:
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"); }
Applicazioni lato client
Per utilizzare il flusso client basato sul browser descritto in Utilizzo di OAuth 2.0 per applicazioni lato client, in genere segui questi passaggi:
- Reindirizza l'utente finale nel browser alla pagina di autorizzazione utilizzando GoogleBrowserClientRequestUrl per concedere all'applicazione browser l'accesso ai dati protetti dell'utente finale.
- Utilizza la libreria client delle API di Google per JavaScript per elaborare il token di accesso trovato nel frammento dell'URL nell'URI di reindirizzamento registrato nella console API di Google.
Esempio di utilizzo 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
Quale libreria utilizzare con Android:
Se sviluppi per Android e l'API di Google che vuoi utilizzare è inclusa nella libreria Google Play Services, utilizza questa libreria per ottenere le migliori prestazioni ed esperienza. Se l'API Google che vuoi utilizzare con Android non fa parte della libreria Google Play Services, puoi utilizzare la libreria client delle API di Google per Java, che supporta Android 4.0 (Ice Cream Sandwich) o versioni successive e che è descritta qui. Il supporto di Android nella libreria client delle API di Google per Java è @Beta.
Informazioni di base:
A partire da Eclair (SDK 2.1), gli account utente vengono gestiti su un dispositivo Android utilizzando Account Manager. L'autorizzazione di tutte le applicazioni Android viene gestita centralmente dall'SDK utilizzando AccountManager. Specifichi l'ambito OAuth 2.0 di cui ha bisogno la tua applicazione e restituisce un token di 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 anche specificare la chiave API dalla console dell'API di Google. In caso contrario, il token fornito da AccountManager ti offre solo una quota anonima, che di solito è molto bassa. Al contrario, specificando una chiave API ricevi una quota senza costi più elevata e puoi, se vuoi, configurare la fatturazione per l'utilizzo superiore.
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; } }