Descripción general
Propósito: Este documento describe las funciones genéricas de OAuth 2.0 que ofrece la biblioteca cliente de Google OAuth para Java. Puedes usar estas funciones para autenticación y autorización para cualquier servicio de Internet.
Para obtener instrucciones sobre el uso de GoogleCredential
para la autorización de OAuth 2.0 con
servicios de Google, consulta
Cómo utilizar OAuth 2.0 con la biblioteca cliente de la API de Google para Java.
Resumen: OAuth 2.0 es una especificación estándar para permitir que los usuarios finales autoricen de forma segura a un cliente de aplicaciones para acceder a los recursos protegidos del servidor. Además, el Token del portador de OAuth 2.0 se explica cómo acceder a esos recursos protegidos a través de un token otorgado durante el proceso de autorización del usuario final.
Para obtener información detallada, consulta la documentación de Javadoc para ver los siguientes paquetes:
- com.google.api.client.auth.oauth2 (de 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)
Registro de clientes
Antes de usar la biblioteca cliente de Google OAuth para Java, es probable que debas registrar tu aplicación con un servidor de autorización para recibir un ID de cliente y el secreto del cliente. (Para obtener información general sobre este proceso, consulta el Cliente Especificación de registro).
Credencial y almacén de credenciales
Credencial
es una clase auxiliar de OAuth 2.0 con protección de subprocesos para acceder a recursos protegidos mediante un
token de acceso. Cuando se usa un token de actualización, Credential
también actualiza el acceso.
cuando venza el token de acceso mediante el token de actualización. Por ejemplo, si
ya tienes un token de acceso, puedes realizar una solicitud de la siguiente manera:
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 mayoría de las aplicaciones deben conservar el token de acceso de la credencial y de actualización para evitar un redireccionamiento futuro a la autorización en el navegador. El CredentialStore la implementación en esta biblioteca dejó de estar disponible y se quitará en el futuro y lanzamientos de versiones. La alternativa es usar el DataStoreFactory y DataStore interfaces con StoredCredential, que proporciona el Biblioteca cliente HTTP de Google para Java.
Puedes usar una de las siguientes implementaciones que proporciona la biblioteca:
- JdoDataStoreFactory conserva la credencial con JDO.
- AppEngineDataStoreFactory conserva la credencial con la API de Google App Engine Data Store.
- MemoryDataStoreFactory “persiste” la credencial en la memoria, lo que solo es útil como durante todo el ciclo de vida del proceso.
- FileDataStoreFactory conserva la credencial en un archivo.
Usuarios de Google App Engine:
AppEngineCredentialStore dejó de estar disponible y se quitará.
Te recomendamos que utilices AppEngineDataStoreFactory con StoredCredential. Si tienes credenciales almacenadas de la forma anterior, puedes usar los métodos auxiliares agregados. migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) migrar.
Usa DataStoreCredentialRefreshListener. y configúrala para la credencial con GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Flujo de código de autorización
Usar el flujo de código de autorización para permitir que el usuario final otorgue tu aplicación el acceso a sus datos protegidos. El protocolo para este flujo se especifica en el Especificación de otorgamiento de código de autorización.
Este flujo se implementa usando AuthorizationCodeFlow. Estos son los pasos:
- Un usuario final accede a tu aplicación. Debes asociar ese usuario con un ID de usuario único para tu aplicación.
- Llamada AuthorizationCodeFlow.loadCredential(String), en función del ID del usuario para comprobar si ya se conocen las credenciales. Si es así, listo.
- De lo contrario, llama a AuthorizationCodeFlow.newAuthorizationUrl() y dirigir el navegador del usuario final a una página de autorización donde pueda otorgar el acceso de tu aplicación a sus datos protegidos.
- Luego, el navegador web redirecciona a la URL de redireccionamiento con un “código”. consulta que se puede usar para solicitar un token de acceso mediante AuthorizationCodeFlow.newTokenRequest(String).
- Usa AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) para almacenar y obtener una credencial para acceder a recursos protegidos.
Por otro lado, si no usas AuthorizationCodeFlow y puedes usar las clases de nivel inferior:
- Usa DataStore.get(String) para cargar la credencial desde la tienda, según el ID del usuario.
- Usa AuthorizationCodeRequestUrl. para dirigir el navegador a la página de autorización.
- Usa AuthorizationCodeResponseUrl. para procesar la respuesta de autorización y analizar el código de autorización.
- Usa AuthorizationCodeTokenRequest para solicitar un token de acceso y, tal vez, un token de actualización.
- Crea una Credential nueva y almacénala con DataStore.set(String, V).
- Acceder a los recursos protegidos con la Credencial. Los tokens de acceso vencidos se actualizan automáticamente mediante el token de actualización, si que corresponda. Asegúrate de usar DataStoreCredentialRefreshListener y configúrala para la credencial con Credential.Builder.addRefreshListener(CredentialRefreshListener).
Flujo de código de autorización del servlet
Esta biblioteca proporciona clases auxiliares de servlet para simplificar significativamente la tarea de código de autorización para casos de uso básicos. Solo proporcionas subclases concretas de AbstractAuthorizationCodeServlet y AbstractAuthorizationCodeCallbackServlet (de google-oauth-client-servlet) y agrégalos a tu archivo web.xml. Ten en cuenta que aún debes ocuparte de las licencias inicio de sesión para tu aplicación web y extraer el ID del usuario.
Código de muestra:
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 } }
Flujo de código de autorización de Google App Engine
El flujo de código de autorización en App Engine es casi idéntico al servlet de código de autorización, con la excepción de que podemos aprovechar la infraestructura API de Usuarios de Java. El usuario debe acceder para que se habilite la API de Java de usuarios. para información sobre cómo redireccionar a los usuarios a una página de acceso si aún no lo están accediste, consulta Seguridad y autenticación (en web.xml).
La diferencia principal
con respecto al caso del servlet
subclases de
AbstractAppEngineAuthorizationCodeServlet y AbstractAppEngineAuthorizationCodeCallbackServlet (de google-oauth-client-appengine). Extienden las clases abstractas de servlet y, luego, implementan el método getUserId
por ti con la API de usuarios de Java. AppEngineDataStoreFactory (de la biblioteca cliente HTTP de Google para Java es una buena opción para conservar la credencial con la API de Data Store de Google App Engine)
Código de muestra:
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(); } }
Flujo de código de autorización de la línea de comandos
Código de ejemplo simplificado tomado de 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); ... }
Flujo de cliente basado en el navegador
Estos son los pasos típicos del flujo de cliente basado en el navegador especificado en el Especificación de otorgamiento implícito:
- Con BrowserClientRequestUrl, redireccionar el navegador del usuario final a la página de autorización donde el usuario final puede otorgarle a tu aplicación acceso a sus datos protegidos.
- Usar una aplicación de JavaScript para procesar el token de acceso que se encuentra en la URL en el URI de redireccionamiento registrado con el servidor de autorización.
Ejemplo de uso para una aplicación 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); }
Cómo detectar un token de acceso vencido
Según la especificación del portador de OAuth 2.0,
Cuando se llama al servidor para acceder a un recurso protegido con un acceso vencido
token, el servidor generalmente responde con un código de estado 401 Unauthorized
HTTP
como los siguientes:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
Sin embargo, parece haber mucha flexibilidad en la especificación. Para consulta la documentación del proveedor de OAuth 2.0.
Un enfoque alternativo es verificar el parámetro expires_in
en el
respuesta de token de acceso.
Esto especifica la vida útil en segundos del token de acceso otorgado, que se
normalmente una hora. Sin embargo, es posible que el token de acceso no caduque al final
de ese período y es posible que el servidor siga permitiendo el acceso. Por eso,
normalmente recomiendan esperar un código de estado 401 Unauthorized
, en lugar de
suponiendo que el token expiró en función del tiempo transcurrido. Como alternativa, puedes
intenta actualizar un token de acceso poco antes de que caduque y, si el servidor de tokens
no está disponible, sigue usando el token de acceso hasta que recibas un 401
. Esta
es la estrategia que se usa de forma predeterminada en
Credencial.
Otra opción es obtener un nuevo token de acceso antes de cada solicitud, pero requiere una solicitud HTTP adicional al servidor de tokens cada vez, por lo que es probable es una mala opción en términos de velocidad y uso de red. Idealmente, almacena el token de acceso en un almacenamiento seguro y persistente para minimizar las solicitudes de acceso nuevo de una aplicación tokens. (Sin embargo, para las aplicaciones instaladas, el almacenamiento seguro es un problema difícil).
Ten en cuenta que un token de acceso puede dejar de ser válido por motivos distintos al vencimiento, Por ejemplo, si el usuario revocó explícitamente el token, asegúrate de que tu de manejo de errores es robusto. Una vez que detectes que un token ya no válido, por ejemplo, si venció o se revocó, debes quitar el acceso token de almacenamiento. En Android, por ejemplo, debes llamar AccountManager.invalidateAuthToken.