Visão geral
Finalidade:este documento descreve as funções genéricas do OAuth 2.0 oferecidas pela biblioteca de cliente do OAuth do Google para Java. É possível usar essas funções para autenticação e autorização em qualquer serviço da Internet.
Para instruções sobre como usar o GoogleCredential
para autorizar o OAuth 2.0 com
os Serviços do Google, consulte
Como usar o OAuth 2.0 com a biblioteca de cliente das APIs do Google para Java.
Resumo:o OAuth 2.0 é uma especificação padrão para permitir que os usuários finais autorizem com segurança um aplicativo cliente a acessar recursos protegidos do lado do servidor. Além disso, a especificação do token de portador do OAuth 2.0 explica como acessar esses recursos protegidos usando um token de acesso concedido durante o processo de autorização do usuário final.
Para mais detalhes, consulte a documentação do Javadoc para os seguintes pacotes:
- com.google.api.client.auth.oauth2 (do google-oauth-client)
- com.google.api.client.extensions.servlet.auth.oauth2 (de google-oauth-client-servlet)
- com.google.api.client.extensions.appengine.auth.oauth2 (from google-oauth-client-appengine)
Registro do cliente
Antes de usar a Biblioteca de cliente OAuth do Google para Java, provavelmente você precisará registrar seu aplicativo em um servidor de autorização para receber um ID e uma chave secreta do cliente. Para informações gerais sobre esse processo, consulte a especificação do registro de clientes.
Armazenamento de credenciais e credenciais
Credential
é uma classe auxiliar do OAuth 2.0 que oferece proteção contra conflitos de threads para acessar recursos protegidos usando um
token de acesso. Ao usar um token de atualização, o Credential
também atualiza o token de acesso
quando ele expira. Por exemplo, se você
já tiver um token de acesso, faça uma solicitação da seguinte maneira:
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(); }
A maioria dos aplicativos precisa manter o token de acesso e de atualização da credencial para evitar um redirecionamento futuro para a página de autorização no navegador. A implementação CredentialStore nesta biblioteca foi descontinuada e será removida em versões futuras. A alternativa é usar as interfaces DataStoreFactory e DataStore com StoredCredential, que são fornecidas pela biblioteca de cliente HTTP do Google para Java (links em inglês).
É possível usar uma das seguintes implementações fornecidas pela biblioteca:
- JdoDataStoreFactory permanece a credencial usando a JDO.
- A AppEngineDataStoreFactory permanece com a credencial usando a API Google App Engine Data Store.
- A MemoryDataStoreFactory "permanece" com a credencial na memória, que é útil apenas como armazenamento de curto prazo para o ciclo de vida do processo.
- FileDataStoreFactory mantém a credencial em um arquivo.
Usuários do Google App Engine:
O AppEngineCredentialStore foi descontinuado e está sendo removido.
Recomendamos que você use o AppEngineDataStoreFactory com StoredCredential. Se você tiver credenciais armazenadas da maneira antiga, use os métodos auxiliares adicionados migrateTo(AppEngineDataStoreFactory) ou migrateTo(DataStore) para migrar.
Use DataStoreCredentialRefreshListener e defina-o para a credencial usando GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Fluxo do código de autorização
Use o fluxo de código de autorização para permitir que o usuário final conceda ao seu aplicativo acesso aos dados protegidos. O protocolo desse fluxo é definido na especificação da concessão de código de autorização.
Esse fluxo é implementado usando o AuthorizationCodeFlow. Essas etapas são:
- Um usuário final faz login no seu aplicativo. É necessário associar esse usuário a um ID exclusivo do seu aplicativo.
- Chame AuthorizationCodeFlow.loadCredential(String), com base no ID do usuário, para verificar se as credenciais do usuário já são conhecidas. Em caso afirmativo, está pronto.
- Caso contrário, chame AuthorizationCodeFlow.newAuthorizationUrl() e direcione o navegador do usuário final para uma página de autorização em que ele possa conceder acesso ao aplicativo aos dados protegidos.
- Em seguida, o navegador da Web redireciona para o URL de redirecionamento com um parâmetro de consulta "code", que pode ser usado para solicitar um token de acesso usando AuthorizationCodeFlow.newTokenRequest(String).
- Use AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) para armazenar e receber uma credencial para acessar recursos protegidos.
Como alternativa, se você não estiver usando o AuthorizationCodeFlow, use as classes de nível inferior:
- Use DataStore.get(String) para carregar a credencial do armazenamento, com base no ID do usuário.
- Use AuthorizationCodeRequestUrl para direcionar o navegador à página de autorização.
- Use AuthorizationCodeResponseUrl para processar a resposta de autorização e analisar o código de autorização.
- Use AuthorizationCodeTokenRequest para solicitar um token de acesso e, possivelmente, um token de atualização.
- Crie uma nova credencial e a armazene usando DataStore.set(String, V).
- Acesse recursos protegidos usando a credencial. Os tokens de acesso expirados são atualizados automaticamente usando o token de atualização, se aplicável. Use DataStoreCredentialRefreshListener e defina-o para a credencial usando Credential.Builder.addRefreshListener(CredentialRefreshListener).
Fluxo do código de autorização do servlet
Essa biblioteca fornece classes auxiliares de servlet para simplificar significativamente o fluxo de código de autorização para casos de uso básicos. Basta fornecer subclasses concretas de AbstractAuthorizationCodeServlet e AbstractAuthorizationCodeCallbackServlet (do google-oauth-client-servlet) e adicioná-las ao arquivo web.xml. Você ainda precisa cuidar do login do usuário no seu aplicativo da Web e extrair um ID de usuário.
Código de exemplo:
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 } }
Fluxo do código de autorização do Google App Engine
O fluxo do código de autorização no App Engine é quase idêntico ao fluxo do código de autorização do servlet, exceto que podemos aproveitar a API Users Java do Google App Engine. O usuário precisa estar conectado para que a API Users Java seja ativada. Para informações sobre como redirecionar usuários a uma página de login caso ainda não tenham feito isso, consulte Segurança e autenticação (em web.xml).
A principal diferença em relação ao caso do servlet é que você fornece subclasses concretas de
AbstractAppEngineAuthorizationCodeServlet e AbstractAppEngineAuthorizationCodeCallbackServlet (do google-oauth-client-appengine). Elas estendem as classes de servlet abstratas e implementam o método getUserId
usando a API Java Users. O AppEngineDataStoreFactory (da biblioteca de cliente HTTP do Google para Java) é uma boa opção para manter a credencial usando a API Data Store do Google App Engine.
Código de exemplo:
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(); } }
Fluxo do código de autorização da linha de comando
Exemplo de código simplificado retirado de dailymotion-cmdline-sample (link em inglês):
/** 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); ... }
Fluxo de cliente baseado em navegador
Estas são as etapas típicas do fluxo de cliente baseado em navegador especificado na especificação de concessão implícita:
- Usando BrowserClientRequestUrl, redirecione o navegador do usuário final para a página de autorização em que ele pode conceder ao seu aplicativo acesso aos dados protegidos.
- Use um aplicativo JavaScript para processar o token de acesso encontrado no fragmento de URL no URI de redirecionamento registrado com o servidor de autorização.
Exemplo de uso para um aplicativo da 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); }
Como detectar um token de acesso expirado
De acordo com a especificação do portador do OAuth 2.0,
quando o servidor é chamado para acessar um recurso protegido com um token de acesso expirado,
ele normalmente responde com um código de status HTTP 401 Unauthorized
,
como este:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
No entanto, parece haver muita flexibilidade na especificação. Para mais detalhes, consulte a documentação do provedor OAuth 2.0.
Uma abordagem alternativa é verificar o parâmetro expires_in
na
resposta do token de acesso.
Isso especifica a vida útil em segundos do token de acesso concedido, que normalmente é
de uma hora. No entanto, o token de acesso pode não expirar no final
desse período, e o servidor pode continuar permitindo o acesso. É por isso que normalmente recomendamos aguardar um código de status 401 Unauthorized
, em vez de presumir que o token expirou com base no tempo decorrido. Como alternativa, tente
atualizar um token de acesso pouco antes do vencimento. Se o servidor de token
estiver indisponível, continue usando o token de acesso até receber um 401
. Essa
é a estratégia usada por padrão em
Credential.
Outra opção é pegar um novo token de acesso antes de cada solicitação, mas isso requer uma solicitação HTTP extra para o servidor de token todas as vezes, portanto, provavelmente é uma escolha ruim em termos de velocidade e uso da rede. O ideal é armazenar o token de acesso em um armazenamento seguro e permanente para minimizar as solicitações de novos tokens de acesso do aplicativo. No entanto, para aplicativos instalados, o armazenamento seguro é um problema difícil.
Um token de acesso pode se tornar inválido por motivos diferentes da expiração, por exemplo, se o usuário tiver revogado o token explicitamente. Portanto, verifique se o código de processamento de erros é robusto. Depois de detectar que um token não é mais válido, por exemplo, se ele tiver expirado ou sido revogado, remova o token de acesso do armazenamento. No Android, por exemplo, é necessário chamar AccountManager.invalidateAuthToken.