Visão geral
Finalidade: este documento explica como usar a classe de utilitário GoogleCredential para fazer autorização do OAuth 2.0 com serviços do Google. Para informações sobre as funções genéricas do OAuth 2.0 que fornecemos, consulte OAuth 2.0 e a biblioteca de cliente do OAuth do Google 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 oferecem suporte a 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 a seu aplicativo cliente e ao proprietário dos dados protegidos que estão sendo acessados. O token de acesso também é associado a um escopo limitado que define o tipo de dados que seu aplicativo cliente tem acesso (por exemplo, "quot;Gerenciar suas tarefas"). Um objetivo importante do OAuth 2.0 é fornecer acesso seguro e conveniente aos dados protegidos e minimizar o possível impacto se um token de acesso for roubado.
Os pacotes do OAuth 2.0 da Biblioteca de cliente da API do Google para Java são criados na Biblioteca de cliente do Google OAuth 2.0 para Java de uso geral.
Veja mais detalhes na 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, você precisa configurar um projeto no Console de APIs do Google para fins de autenticação e faturamento, seja seu cliente instalado ou não, um aplicativo para dispositivos móveis, um servidor da Web ou um cliente em execução no navegador.
Para instruções sobre como configurar suas credenciais corretamente, consulte a Ajuda do Console de APIs.
Credencial
GoogleCredential
O GoogleCredential é uma classe auxiliar thread-safe para o 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 do 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 retirado 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(); }
Armazenamento de dados
Normalmente, o token de acesso tem uma data de validade de uma hora.
Depois desse período, ocorrerá um erro se você tentar usá-lo.
A GoogleCredential
cuida automaticamente do token para atualizá-lo. Isso significa simplesmente
que você receba um novo token de acesso. Isso é feito com um token de atualização de longa duração, que normalmente é recebido com o token de acesso se você usar o parâmetro access_type=offline
durante o fluxo do código de autorização. Consulte GoogleAuthorizationCodeFlow.Builder.setAccessType(String).
A maioria dos aplicativos precisará manter o token de acesso e/ou o token de atualização da credencial. Para manter os tokens de acesso e/ou atualização das credenciais, forneça sua própria implementação de DataStoreFactory com StoredCredential ou use uma das implementações a seguir fornecidas pela biblioteca:
- AppEngineDataStoreFactory: mantém a credencial usando a API Google App Engine Data Store.
- MemoryDataStoreFactory: "quot;persists" a credencial na memória, que é útil apenas como um armazenamento de curto prazo durante todo o ciclo de vida do processo.
- FileDataStoreFactory: mantém a credencial em um arquivo.
Usuários do AppEngine: o AppEngineCredentialStore foi descontinuado e será removido em breve. Recomendamos que você use o AppEngineDataStoreFactory com o StoredCredential. Se você tiver credenciais armazenadas de maneira antiga, use os métodos auxiliares adicionados 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 do código de autorização para permitir que o usuário final conceda ao seu aplicativo acesso aos dados protegidos nas APIs do Google. O protocolo desse 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. É necessário associar esse usuário a um ID de usuário 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. Em caso positivo, está tudo pronto.
- Caso contrário, chame AuthorizationCodeFlow.newAuthorizationUrl() e direcione o navegador do usuário final a uma página de autorização para conceder ao seu aplicativo acesso aos dados protegidos dele.
- O servidor de autorização do Google redirecionará o navegador de volta para o URL de redirecionamento especificado pelo aplicativo, junto com um parâmetro de consulta
code
. Use o parâmetrocode
para solicitar um token de acesso com o 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 GoogleAuthorizationCodeFlow, poderá usar as classes de nível inferior:
- 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 sã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 você está usando. Para ver mais detalhes, consulte Como configurar o OAuth 2.0 e os Cenários do OAuth 2.0. Veja abaixo os snippets de código para cada um dos fluxos.
Aplicativos do servidor da Web
O protocolo desse 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 seu arquivo web.xml. Observe que você ainda precisa cuidar do login do usuário no seu aplicativo da Web e extrair um ID do 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 de código de autorização do servlet, exceto pelo fato de podermos aproveitar a API Users Java do Google App Engine. O usuário precisa fazer login para que a API de usuários do Java seja ativada. Para informações sobre como 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 abstratas de servlet e implementam o método getUserId
para você usando a API Java do usuário. O AppEngineDataStoreFactory
(de google-http-client-appengine)
é uma boa opção para manter a credencial usando a API Google App Engine
DataStore.
Exemplo retirado (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 outra amostra, consulte storage-serviceaccount-appengine-sample.
Contas de serviço
A GoogleCredential também oferece suporte a contas de serviço. Ao contrário da credencial em que um aplicativo cliente solicita acesso aos dados do usuário final, as contas de serviço fornecem acesso aos dados do próprio aplicativo cliente. O aplicativo cliente assina a solicitação de um token de acesso usando uma chave privada salva no Console de APIs do Google.
Exemplo de código retirado de plus-serviceaccount-cmdline-sample:
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); 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 outra amostra, consulte storage-serviceaccount-cmdline-sample.
Falsificação de identidade
Também é possível usar o fluxo da conta de serviço para representar um usuário em um domínio de sua propriedade. Esse fluxo é muito semelhante ao fluxo de conta de serviço acima, mas também é chamado de GoogleCredential.Builder.setServiceAccountUser(String).
Apps instalados
Este é o fluxo do código de autorização de linha de comando descrito em Usar o OAuth 2.0 para aplicativos instalados.
Snippet de exemplo de plus-cmdline-sample:
public static void main(String[] args) { try { httpTransport = GoogleNetHttpTransport.newTrustedTransport(); 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, geralmente siga estas etapas:
- Redirecione o usuário final no navegador à página de autorização usando GoogleBrowserClientRequestUrl para conceder ao seu aplicativo de navegador acesso aos dados protegidos do usuário final.
- Use a Biblioteca de cliente da API do Google para JavaScript para processar o token de acesso encontrado no fragmento de URL no URI de redirecionamento registrado no Console de 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 você 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, será possível usar a biblioteca de cliente da API do Google para Java, compatível com o Android 4.0 (Ice Cream Sandwich) (ou versões mais recentes), descrita a seguir. A compatibilidade do Android na Biblioteca de clientes de APIs do Google para Java é @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 apps Android é gerenciada centralmente pelo SDK usando o AccountManager. Você especifica o escopo do OAuth 2.0 que seu aplicativo precisa e retorna um token de acesso a ser usado.
O escopo do OAuth 2.0 é especificado pelo parâmetro authTokenType
como oauth2:
mais o escopo. Exemplo:
oauth2:https://www.googleapis.com/auth/tasks
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,
"quot;Gerenciar suas tarefas" é um alias para o exemplo de authtokenType
mostrado acima.
Também é necessário especificar a chave de API do Console de APIs do Google. Caso contrário, o token fornecido pelo AccountManager só fornecerá uma 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, como opção, pode configurar o faturamento para uso acima dela.
Exemplo de snippet de código retirado 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; } }