Visão geral
Objetivo:este documento explica como usar a classe utilitária GoogleCredential para fazer a autorização do OAuth 2.0 com os serviços do Google. Para informações sobre as funções genéricas do OAuth 2.0 que oferecemos, consulte OAuth 2.0 e a biblioteca de cliente do Google OAuth para Java.
Resumo:para acessar dados protegidos armazenados nos serviços do Google, use o OAuth 2.0 para autorização. As APIs do Google são compatíveis com fluxos do OAuth 2.0 para diferentes tipos de aplicativos cliente. Em todos esses fluxos, o aplicativo cliente solicita um token de acesso associado apenas ao seu aplicativo cliente e ao proprietário dos dados protegidos que estão sendo acessados. O token de acesso também está associado a um escopo limitado que define o tipo de dados a que o aplicativo cliente tem acesso (por exemplo, "Gerenciar suas tarefas"). Um objetivo importante do OAuth 2.0 é fornecer acesso seguro e conveniente aos dados protegidos, minimizando o impacto potencial se um token de acesso for roubado.
Os pacotes do OAuth 2.0 na biblioteca de cliente das APIs do Google para Java são criados na biblioteca de cliente do Google OAuth 2.0 para Java (link em inglês) de uso geral.
Para mais detalhes, consulte a documentação do Javadoc para os seguintes pacotes:
- com.google.api.client.googleapis.auth.oauth2 (de google-api-client)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (de google-api-client-appengine)
Console de APIs do Google
Antes de acessar as APIs do Google, configure um projeto no Console de APIs do Google para fins de autenticação e faturamento, seja seu cliente um aplicativo instalado, um aplicativo móvel, um servidor da Web ou um cliente executado no navegador.
Para instruções sobre como configurar suas credenciais corretamente, consulte a Ajuda do console de APIs.
Credencial
GoogleCredential
GoogleCredential é uma classe auxiliar thread-safe para OAuth 2.0 que acessa recursos protegidos usando um token de acesso. Por exemplo, se você já tiver um token de acesso, poderá fazer uma solicitação da seguinte maneira:
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken); Plus plus = new Plus.builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), credential) .setApplicationName("Google-PlusSample/1.0") .build();
Identidade do Google App Engine
Essa credencial alternativa é baseada na API Java App Identity do Google App Engine. Ao contrário da credencial em que um aplicativo cliente solicita acesso aos dados de um usuário final, a API App Identity fornece acesso aos dados do próprio aplicativo cliente.
Use AppIdentityCredential (de google-api-client-appengine). Essa credencial é muito mais simples porque o Google App Engine cuida de todos os detalhes. Especifique apenas o escopo do OAuth 2.0 necessário.
Exemplo de código extraído de 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(); }
Repositório de dados
Um token de acesso normalmente tem uma data de expiração de uma hora. Depois desse prazo, você vai receber um erro se tentar usá-lo.
A biblioteca GoogleCredential se encarrega de atualizar automaticamente o token, o que significa simplesmente receber um novo token de acesso. Isso é feito por um token de atualização de longa duração, que geralmente é recebido com o token de acesso se você usar o parâmetro access_type=offline durante o fluxo de código de autorização. Consulte GoogleAuthorizationCodeFlow.Builder.setAccessType(String).
A maioria dos aplicativos precisa manter o token de acesso e/ou de atualização da credencial. Para persistir os tokens de acesso e/ou atualização da credencial, você pode fornecer sua própria implementação de DataStoreFactory com StoredCredential; ou usar uma das seguintes implementações fornecidas pela biblioteca:
- AppEngineDataStoreFactory: persiste a credencial usando a API Google App Engine Data Store.
- MemoryDataStoreFactory: "persiste" a credencial na memória, o que só é útil como um armazenamento de curto prazo durante o ciclo de vida do processo.
- FileDataStoreFactory: persiste a credencial em um arquivo.
Usuários do App Engine:o AppEngineCredentialStore foi descontinuado e será removido em breve. Recomendamos que você use AppEngineDataStoreFactory com StoredCredential. Se você tiver credenciais armazenadas da maneira antiga, use os métodos auxiliares migrateTo(AppEngineDataStoreFactory) ou migrateTo(DataStore) para fazer a migração.
É possível usar DataStoreCredentialRefreshListener e definir 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 aplicativo acesso aos dados protegidos dele nas APIs do Google. O protocolo para esse fluxo é especificado em Concessão de código de autorização.
Esse fluxo é implementado usando GoogleAuthorizationCodeFlow. Essas etapas são:
- O usuário final faz login no seu aplicativo. Você precisa associar esse usuário a um User ID exclusivo para seu aplicativo.
- Chame AuthorizationCodeFlow.loadCredential(String)) com base no ID do usuário para verificar se as credenciais do usuário final já são conhecidas. Se sim, terminamos.
- Caso contrário, chame AuthorizationCodeFlow.newAuthorizationUrl() e direcione o navegador do usuário final para uma página de autorização para conceder ao seu aplicativo acesso aos dados protegidos dele.
- Em seguida, o servidor de autorização do Google redireciona o navegador de volta para o
URL de redirecionamento especificado pelo aplicativo, junto com um parâmetro de consulta
code. Use o parâmetrocodepara 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 GoogleAuthorizationCodeFlow, use as classes de nível mais baixo:
- Use DataStore.get(String) para carregar a credencial da loja com base no ID do usuário.
- Use GoogleAuthorizationCodeRequestUrl 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 GoogleAuthorizationCodeTokenRequest para solicitar um token de acesso e, possivelmente, um token de atualização.
- Crie uma nova GoogleCredential e armazene-a usando DataStore.set(String, V).
- Acesse recursos protegidos usando o
GoogleCredential. Os tokens de acesso expirados serão atualizados automaticamente usando o token de atualização (se aplicável). Use DataStoreCredentialRefreshListener e defina-o para a credencial usando GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener).
Ao configurar seu projeto no Console de APIs do Google, você seleciona entre diferentes credenciais, dependendo do fluxo que está usando. Para mais detalhes, consulte Como configurar o OAuth 2.0 e Cenários do OAuth 2.0. Confira abaixo snippets de código para cada um dos fluxos.
Aplicativos do servidor da Web
O protocolo para esse fluxo é explicado em Como usar o OAuth 2.0 para aplicativos de servidor da Web.
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 (de google-oauth-client-servlet) e adicioná-las ao arquivo web.xml. Você ainda precisa cuidar do login do usuário no aplicativo da Web e extrair um ID de usuário.
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 } }
Aplicativos 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 Java Users do Google App Engine. O usuário precisa fazer login para que a API Java Users seja ativada. Para informações sobre redirecionar usuários para uma página de login se eles ainda não estiverem conectados, consulte Segurança e autenticação (em web.xml).
A principal diferença do caso do servlet é que você fornece subclasses concretas de AbstractAppEngineAuthorizationCodeServlet e AbstractAppEngineAuthorizationCodeCallbackServlet (de google-oauth-client-appengine.
Eles estendem as classes de servlet abstratas e implementam o método getUserId
para você usando a API Java Users. AppEngineDataStoreFactory
(de google-http-client-appengine)
é uma boa opção para persistir a credencial usando a API Google App Engine Data
Store.
Exemplo extraído (ligeiramente modificado) de 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(); } }
Para ver outro exemplo, consulte storage-serviceaccount-appengine-sample.
Contas de serviço
O GoogleCredential também é compatível com contas de serviço. Ao contrário da credencial em que um aplicativo cliente solicita acesso aos dados de um usuário final, as contas de serviço fornecem acesso aos próprios dados do aplicativo cliente. O aplicativo cliente assina a solicitação de um token de acesso usando uma chave privada baixada do Console de APIs do Google.
Exemplo de uso:
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(); ...
Para ver outro exemplo, consulte storage-serviceaccount-cmdline-sample.
Falsificação de identidade
Também é possível usar o fluxo de conta de serviço para representar um usuário em um domínio que você possui. Isso é muito semelhante ao fluxo de conta de serviço acima, mas você também chama GoogleCredential.Builder.setServiceAccountUser(String).
Aplicativos instalados
Esse é o fluxo de código de autorização da linha de comando descrito em Como usar o OAuth 2.0 para aplicativos instalados.
Exemplo de uso:
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"); }
Aplicativos do lado do cliente
Para usar o fluxo de cliente baseado em navegador descrito em Como usar o OAuth 2.0 para aplicativos do lado do cliente, siga estas etapas:
- Redirecione o usuário final no navegador para a página de autorização usando GoogleBrowserClientRequestUrl para conceder ao aplicativo do navegador acesso aos dados protegidos do usuário final.
- Use a Biblioteca de cliente das APIs do Google para JavaScript para processar o token de acesso encontrado no fragmento de URL no URI de redirecionamento registrado no Console das APIs do Google.
Exemplo de uso para um aplicativo da 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
Qual biblioteca usar com o Android:
Se você estiver desenvolvendo para Android e a API do Google que quer usar estiver incluída na biblioteca do Google Play Services, use essa biblioteca para ter o melhor desempenho e experiência. Se a API do Google que você quer usar com o Android não fizer parte da biblioteca do Google Play Services, use a biblioteca de cliente das APIs do Google para Java, que é compatível com o Android 4.0 (Ice Cream Sandwich) ou versões mais recentes e é descrita aqui. O suporte para Android na biblioteca de cliente da API do Google para Java está em @Beta.
Histórico
A partir do Eclair (SDK 2.1), as contas de usuário são gerenciadas em um dispositivo Android usando o Gerenciador de contas. Toda a autorização de aplicativos Android é gerenciada centralmente pelo SDK usando o AccountManager. Você especifica o escopo do OAuth 2.0 de que seu aplicativo precisa, e ele retorna um token de acesso para uso.
O escopo do OAuth 2.0 é especificado pelo parâmetro authTokenType como oauth2:
mais o escopo. Exemplo:
oauth2:https://www.googleapis.com/auth/tasks
Isso especifica o acesso de leitura/gravação à API Google Tasks. Se você precisar de vários escopos do OAuth 2.0, use uma lista separada por espaços.
Algumas APIs têm parâmetros authTokenType especiais que também funcionam. Por exemplo, "Gerenciar suas tarefas" é um alias para o exemplo authtokenType mostrado acima.
Você também precisa especificar a chave de API do Console de APIs do Google. Caso contrário, o token fornecido pelo AccountManager só oferece cota anônima, que geralmente é muito baixa. Por outro lado, ao especificar uma chave de API, você recebe uma cota sem custo financeiro maior e pode configurar o faturamento para uso acima desse limite.
Exemplo de snippet de código extraído de 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; } }