Panoramica
Scopo: questo documento descrive le funzioni generiche di OAuth 2.0 offerte da la libreria client OAuth di Google per Java. Puoi utilizzare queste funzioni per l'autenticazione e l'autorizzazione per qualsiasi servizio Internet.
Per istruzioni sull'utilizzo di GoogleCredential
per l'autorizzazione di OAuth 2.0 con
per i servizi Google, vedi
Utilizzo di OAuth 2.0 con la libreria client dell'API di Google per Java.
Riepilogo: OAuth 2.0 è un specifica standard per consentire agli utenti finali di autorizzare in modo sicuro un client per accedere a risorse lato server protette. Inoltre, Token di connessione OAuth 2.0 spiega come accedere a tali risorse protette utilizzando un modello di accesso richiesto durante il processo di autorizzazione dell'utente finale.
Per maggiori dettagli, consulta la documentazione Javadoc per i seguenti pacchetti:
- com.google.api.client.auth.oauth2 (da google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (from google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Registrazione client
Prima di utilizzare la libreria client OAuth di Google per Java, probabilmente dovrai registrare l'applicazione con un server di autorizzazione per ricevere un ID client e client secret. Per informazioni generali su questo processo, consulta Cliente specifiche per la registrazione.)
Archivio credenziali e credenziali
Credenziale
è una classe helper OAuth 2.0 sicura per i thread che consente di accedere alle risorse protette utilizzando una
token di accesso. Quando utilizzi un token di aggiornamento, Credential
aggiorna anche l'accesso
quando il token di accesso scade utilizzando il token di aggiornamento. Ad esempio, se
disponi già di un token di accesso, puoi effettuare una richiesta nel seguente modo:
public static HttpResponse executeGet( HttpTransport transport, JsonFactory jsonFactory, String accessToken, GenericUrl url) throws IOException { Credential credential = new Credential(BearerToken.authorizationHeaderAccessMethod()).setAccessToken(accessToken); HttpRequestFactory requestFactory = transport.createRequestFactory(credential); return requestFactory.buildGetRequest(url).execute(); }
La maggior parte delle applicazioni deve rendere permanente il token di accesso delle credenziali di aggiornamento per evitare un reindirizzamento futuro all'autorizzazione nel browser. La CredentialStore l'implementazione in questa libreria è deprecata e verrà rimossa in futuro release. L'alternativa è utilizzare DataStoreFactory e DataStore si interfaccia con StoredCredential, che vengono forniti Libreria client HTTP di Google per Java.
Puoi utilizzare una delle seguenti implementazioni fornite dalla libreria:
- JdoDataStoreFactory rende permanente la credenziale utilizzando JDO.
- AppEngineDataStoreFactory rende permanente la credenziale utilizzando l'API Google App Engine Data Store.
- MemoryDataStoreFactory "persiste" la credenziale in memoria, cosa utile solo per i prompt spazio di archiviazione per tutta la durata del processo.
- FileDataStoreFactory rende la credenziale permanente in un file.
Utenti di Google App Engine:
L'API AppEngineCredentialStore è deprecata e verrà rimossa.
Ti consigliamo di utilizzare AppEngineDataStoreFactory con StoredCredential. Se hai memorizzato le credenziali nel modo precedente, puoi usare i metodi helper aggiunti migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) di cui eseguire la migrazione.
Utilizza DataStoreCredentialRefreshListener e la imposti 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 l'accesso ai propri dati protetti. Il protocollo per questo flusso è specificato nel Specifica per la concessione del codice di autorizzazione.
Questo flusso viene implementato utilizzando AuthorizationCodeFlow. I passaggi sono:
- Un utente finale accede alla tua applicazione. Devi 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 sono già note. Se è così, non devi fare altro.
- In caso contrario, chiama AuthorizationCodeFlow.newAuthorizationUrl() e indirizzare il browser dell'utente finale a una pagina di autorizzazione in cui può concedere l'accesso della tua applicazione ai propri dati protetti.
- Il browser web reindirizza quindi all'URL di reindirizzamento con un "codice" query parametro che può essere utilizzato per richiedere un token di accesso utilizzando AuthorizationCodeFlow.newTokenRequest(String).
- Utilizza le funzionalità di AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) per archiviare e ottenere una credenziale per accedere alle risorse protette.
In alternativa, se non utilizzi AuthorizationCodeFlow puoi utilizzare le classi di livello inferiore:
- Utilizza DataStore.get(String) per caricare la credenziale dallo store, in base all'ID utente.
- Utilizza AuthorizationCodeRequestUrl per indirizzare il browser alla pagina di autorizzazione.
- Utilizza AuthorizationCodeResponseUrl per elaborare la risposta di autorizzazione e analizzare il codice di autorizzazione.
- Utilizza AuthorizationCodeTokenRequest per richiedere un token di accesso ed eventualmente un token di aggiornamento.
- Crea una nuova credenziale e memorizzala utilizzando DataStore.set(String, V).
- Accedi alle risorse protette utilizzando la credenziale. I token di accesso scaduti vengono aggiornati automaticamente utilizzando il token di aggiornamento, se applicabile. Assicurati di utilizzare DataStoreCredentialRefreshListener e la imposti per la credenziale utilizzando Credential.Builder.addRefreshListener(CredentialRefreshListener).
Flusso del codice di autorizzazione servlet
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.
Codice di esempio:
public class ServletSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } } public class ServletCallbackSample 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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new NetHttpTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialDataStore( StoredCredential.getDefaultDataStore( new FileDataStoreFactory(new File("datastoredir")))) .build(); } @Override protected String getUserId(HttpServletRequest req) throws ServletException, IOException { // return user ID } }
Flusso del codice di autorizzazione 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. Per abilitare l'API Users Java, l'utente deve aver eseguito l'accesso. della informazioni sul reindirizzamento a una pagina di accesso, se non lo sono già se hai 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
per te utilizzando l'API Users Java. AppEngineDataStoreFactory (dalla libreria client HTTP di Google per Java è una buona opzione per mantenere la credenziale utilizzando l'API Google App Engine Data Store).
Codice di esempio:
public class AppEngineSample extends AbstractAppEngineAuthorizationCodeServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } } public class AppEngineCallbackSample extends AbstractAppEngineAuthorizationCodeCallbackServlet { @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 AuthorizationCodeFlow.Builder(BearerToken.authorizationHeaderAccessMethod(), new UrlFetchTransport(), new JacksonFactory(), new GenericUrl("https://server.example.com/token"), new BasicAuthentication("s6BhdRkqt3", "7Fjfp0ZBr1KtDRbnfVdmIw"), "s6BhdRkqt3", "https://server.example.com/authorize").setCredentialStore( StoredCredential.getDefaultDataStore(AppEngineDataStoreFactory.getDefaultInstance())) .build(); } }
Flusso del codice di autorizzazione dalla riga di comando
Esempio di codice semplificato tratto da dailymotion-cmdline-sample:
/** Authorizes the installed application to access user's protected data. */ private static Credential authorize() throws Exception { OAuth2ClientCredentials.errorIfNotSpecified(); // set up authorization code flow AuthorizationCodeFlow flow = new AuthorizationCodeFlow.Builder(BearerToken .authorizationHeaderAccessMethod(), HTTP_TRANSPORT, JSON_FACTORY, new GenericUrl(TOKEN_SERVER_URL), new ClientParametersAuthentication( OAuth2ClientCredentials.API_KEY, OAuth2ClientCredentials.API_SECRET), OAuth2ClientCredentials.API_KEY, AUTHORIZATION_SERVER_URL).setScopes(Arrays.asList(SCOPE)) .setDataStoreFactory(DATA_STORE_FACTORY).build(); // authorize LocalServerReceiver receiver = new LocalServerReceiver.Builder().setHost( OAuth2ClientCredentials.DOMAIN).setPort(OAuth2ClientCredentials.PORT).build(); return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } private static void run(HttpRequestFactory requestFactory) throws IOException { DailyMotionUrl url = new DailyMotionUrl("https://api.dailymotion.com/videos/favorites"); url.setFields("id,tags,title,url"); HttpRequest request = requestFactory.buildGetRequest(url); VideoFeed videoFeed = request.execute().parseAs(VideoFeed.class); ... } public static void main(String[] args) { ... DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR); final Credential credential = authorize(); HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() { @Override public void initialize(HttpRequest request) throws IOException { credential.initialize(request); request.setParser(new JsonObjectParser(JSON_FACTORY)); } }); run(requestFactory); ... }
Flusso client basato su browser
Questi sono i passaggi tipici del flusso client basato su browser specificato nel Specifica di concessione implicita:
- Con BrowserClientRequestUrl, reindirizzare il browser dell'utente finale alla pagina di autorizzazione dove l'utente finale può concedere alla tua applicazione l'accesso ai propri dati protetti.
- Utilizza un'applicazione JavaScript per elaborare il token di accesso trovato nell'URL all'URI di reindirizzamento registrato presso il server di autorizzazione.
Utilizzo di esempio per un'applicazione web:
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = new BrowserClientRequestUrl( "https://server.example.com/authorize", "s6BhdRkqt3").setState("xyz") .setRedirectUri("https://client.example.com/cb").build(); response.sendRedirect(url); }
Rilevamento di un token di accesso scaduto
In base alla specifica di connessione OAuth 2.0,
quando il server viene chiamato per accedere a una risorsa protetta con accesso scaduto.
il server risponde con un codice di stato HTTP 401 Unauthorized
ad esempio:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Tuttavia, sembra esserci molta flessibilità nella specifica. Per consulta la documentazione del provider OAuth 2.0.
Un approccio alternativo consiste nel controllare il parametro expires_in
nella
risposta del token di accesso.
Specifica la durata in secondi del token di accesso concesso, ovvero
di solito un'ora. Tuttavia, il token di accesso potrebbe non scadere alla fine
di quel periodo e il server potrebbe continuare a consentire l'accesso. Ecco perché
in genere consigliamo di attendere il codice di stato 401 Unauthorized
, anziché
sempre che il token sia scaduto in base al tempo trascorso. In alternativa, puoi
prova ad aggiornare un token di accesso poco prima che scada e se il server
non è disponibile, continua a utilizzare il token di accesso finché non ricevi un 401
. Questo
è la strategia utilizzata per impostazione predefinita
Credenziale.
Un'altra opzione è ottenere un nuovo token di accesso prima di ogni richiesta, ma richiede una richiesta HTTP aggiuntiva al server token ogni volta, quindi probabilmente scelta scadente in termini di velocità e utilizzo della rete. Idealmente, memorizza il token di accesso nello spazio di archiviazione permanente sicuro per ridurre al minimo le richieste di un nuovo accesso da parte di un'applicazione. di token. Per le applicazioni installate, però, l'archiviazione sicura rappresenta un problema difficile.
Tieni presente che un token di accesso potrebbe non essere più valido per motivi diversi dalla scadenza, ad esempio se l'utente ha revocato il token in modo esplicito, quindi assicurati che per la gestione degli errori è efficace. Una volta rilevato che un token non è più valido, ad esempio se è scaduto o è stato revocato, devi rimuovere l'accesso dal tuo spazio di archiviazione. Su Android, ad esempio, devi chiamare AccountManager.invalidateAuthToken.