无法使用 Google API [google-oauth-java-client-1.12.0-beta] 获取服务帐户流程的令牌 [英] Unable to get token using Google APIs [google-oauth-java-client-1.12.0-beta] for service account flow

查看:20
本文介绍了无法使用 Google API [google-oauth-java-client-1.12.0-beta] 获取服务帐户流程的令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Google API(版本 google-oauth-java-client-1.12.0-beta)获取 OAuth2 访问令牌,但返回invalid_grant".参考:https://developers.google.com/accounts/docs/OAuth2ServiceAccount>

代码如下:

import com.google.api.client.auth.jsontoken.JsonWebSignature;导入 com.google.api.client.auth.jsontoken.JsonWebToken;导入 com.google.api.client.auth.jsontoken.RsaSHA256Signer;导入 com.google.api.client.auth.oauth2.TokenRequest;导入 com.google.api.client.auth.oauth2.TokenResponse;导入 com.google.api.client.http.GenericUrl;导入 com.google.api.client.http.HttpTransport;导入 com.google.api.client.http.javanet.NetHttpTransport;导入 com.google.api.client.json.JsonFactory;导入 com.google.api.client.json.jackson2.JacksonFactory;导入 com.google.api.client.util.Clock;导入 java.io.FileInputStream;导入 java.io.IOException;导入 java.security.GeneralSecurityException;导入 java.security.KeyStore;导入 java.security.KeyStoreException;导入 java.security.NoSuchAlgorithmException;导入 java.security.PrivateKey;导入 java.security.UnrecoverableKeyException;导入 java.security.cert.CertificateException;公共类 TestClient{private static PrivateKey getPrivateKey(String keyFile, String alias, String password)抛出 KeyStoreException、IOException、NoSuchAlgorithmException、CertificateException、UnrecoverableKeyException{KeyStore keystore = KeyStore.getInstance("PKCS12");keystore.load(new FileInputStream(keyFile), password.toCharArray());PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray());返回私钥;}public static void main(String[] args)抛出 GeneralSecurityException、IOException{String password = "notasecret";String alias = "privatekey";String keyFile = "<私钥文件>.p12";String serviceAccountScopes = "https://www.googleapis.com/auth/urlshortener";String serviceAccountUser = "user1@gmail.com";String serviceAccountId = ".apps.googleusercontent.com";JsonWebSignature.Header header = new JsonWebSignature.Header();header.setAlgorithm("RS256");header.setType("JWT");JsonWebToken.Payload payload = new JsonWebToken.Payload(Clock.SYSTEM);long currentTime = Clock.SYSTEM.currentTimeMillis();payload.setIssuer(serviceAccountId).setAudience("https://accounts.google.com/o/oauth2/token").setIssuedAtTimeSeconds(currentTime/1000).setExpirationTimeSeconds(currentTime/1000 + 3600).setPrincipal(serviceAccountUser);payload.put("scope", serviceAccountScopes);System.out.println(payload.toPrettyString());PrivateKey serviceAccountPrivateKey = getPrivateKey(keyFile, alias, password);字符串断言 = RsaSHA256Signer.sign(serviceAccountPrivateKey, getJsonFactory(), header, payload);TokenRequest request = new TokenRequest(getTransport(), getJsonFactory(), new GenericUrl(getTokenServerEncodedUrl()), "assertion");request.put("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");request.put("断言", 断言);TokenResponse resp = request.execute();System.out.println("令牌:" + resp.getAccessToken());}私有静态字符串 getTokenServerEncodedUrl(){返回https://accounts.google.com/o/oauth2/token";}私有静态 JsonFactory getJsonFactory(){返回新的 JacksonFactory();}私有静态 HttpTransport getTransport(){返回新的 NetHttpTransport();}}

结果:

线程main"com.google.api.client.auth.oauth2.TokenResponseException 中的异常:400 错误请求{错误":invalid_grant"}在 com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:103)在 com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:303)在 com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:323)

这里有什么问题?任何提示将不胜感激.

在 Google 服务和 Google API 客户端库上使用服务帐户时,您不必自己创建签名并构建 JWT 令牌,因为有阅读- 使用实用程序类通过 OAuth 2.0 简单地执行服务帐户授权.

然而,除了包含多种编程语言的详细解释和代码示例的 Google Drive 文档外,这并没有得到很好的记录.你应该阅读:https://developers.google.com/drive/service-accounts#google_apis_console_project_service_accounts

您的代码存在一些问题:

  • 服务帐户的 ID 应采用以下格式:<some-id>@developer.gserviceaccount.com(是的,电子邮件而不是客户 ID,我知道这很奇怪)
  • 您只能在执行 Google Apps 域范围委托时设置principal,但您可以当然,不要在 Gmail 帐户上这样做,只能在管理员授予您访问其域的 Google Apps 帐户上这样做,因此在您的情况下:不要设置它.

以下是带有 Java 服务帐户的 OAuth 2.0 的代码示例.

注意:您还需要下载URL Shortener 库.

/** 服务账号邮箱 */private static final String SERVICE_ACCOUNT_EMAIL = "@developer.gserviceaccount.com";/** 服务账户的私钥文件路径 */private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "/path/to/-privatekey.p12";/*** 构建并返回使用服务帐户授权的 URL Shortner 服务对象.** @return URL Shortner 服务对象,准备发出请求.*/公共静态驱动器 getDriveService() 抛出 GeneralSecurityException、IOException、URISyntaxException {HttpTransport httpTransport = new NetHttpTransport();JacksonFactory jsonFactory = new JacksonFactory();GoogleCredential 凭证 = new GoogleCredential.Builder().setTransport(httpTransport).setJsonFactory(jsonFactory).setServiceAccountId(SERVICE_ACCOUNT_EMAIL).setServiceAccountScopes(UrlshortenerScopes.URLSHORTENER).setServiceAccountPrivateKeyFromP12File(新的 java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH)).建造();Urlshortener service = new Urlshortener.Builder(httpTransport, jsonFactory, null).setHttpRequestInitializer(credential).build();退货服务;}

I am using Google APIs (version google-oauth-java-client-1.12.0-beta) to get a OAuth2 access token but got back "invalid_grant". Ref: https://developers.google.com/accounts/docs/OAuth2ServiceAccount

Here is the code:

import com.google.api.client.auth.jsontoken.JsonWebSignature;
import com.google.api.client.auth.jsontoken.JsonWebToken;
import com.google.api.client.auth.jsontoken.RsaSHA256Signer;
import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Clock;

import java.io.FileInputStream;
import java.io.IOException;

import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;


public class TestClient
{
  private static PrivateKey getPrivateKey(String keyFile, String alias, String password)
    throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
  {
    KeyStore keystore = KeyStore.getInstance("PKCS12");
    keystore.load(new FileInputStream(keyFile), password.toCharArray());
    PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray());

    return privateKey;
  }

  public static void main(String[] args)
    throws GeneralSecurityException, IOException
  {
    String password = "notasecret";
    String alias = "privatekey";
    String keyFile = "<private key file>.p12";
    String serviceAccountScopes = "https://www.googleapis.com/auth/urlshortener";
    String serviceAccountUser = "user1@gmail.com";
    String serviceAccountId = "<a/c id>.apps.googleusercontent.com";
    JsonWebSignature.Header header = new JsonWebSignature.Header();
    header.setAlgorithm("RS256");
    header.setType("JWT"); 

    JsonWebToken.Payload payload = new JsonWebToken.Payload(Clock.SYSTEM);
    long currentTime = Clock.SYSTEM.currentTimeMillis();
    payload.setIssuer(serviceAccountId)
       .setAudience("https://accounts.google.com/o/oauth2/token")
       .setIssuedAtTimeSeconds(currentTime / 1000)
       .setExpirationTimeSeconds(currentTime / 1000 + 3600)
       .setPrincipal(serviceAccountUser);
    payload.put("scope", serviceAccountScopes); 
    System.out.println(payload.toPrettyString());

    PrivateKey serviceAccountPrivateKey = getPrivateKey(keyFile, alias, password);
    String assertion = RsaSHA256Signer.sign(serviceAccountPrivateKey, getJsonFactory(), header, payload);     
    TokenRequest request = new TokenRequest(getTransport(), getJsonFactory(), new GenericUrl(getTokenServerEncodedUrl()), "assertion");     

    request.put("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");     
    request.put("assertion", assertion);     
    TokenResponse resp = request.execute();    
    System.out.println("token : " + resp.getAccessToken());
  }

  private static String getTokenServerEncodedUrl()
  {
    return "https://accounts.google.com/o/oauth2/token";
  }

  private static JsonFactory getJsonFactory()
  {
    return new JacksonFactory();
  }

  private static HttpTransport getTransport()
  {
    return new NetHttpTransport();
  }
}

Result:

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
  "error" : "invalid_grant"
}
    at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:103)
    at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:303)
    at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:323)

What is the problem here? any hint would be appreciated.

解决方案

When using Service Accounts on Google Services and the Google APIs Client library you don't have to create the signature and construct the JWT Token by yourself as there are read-to-use utility classes to simply perform Service accounts authorization via OAuth 2.0.

However this is not very well documented except on the Google Drive documentation which contains detailed explanation and code samples in multiple programming languages. You should read: https://developers.google.com/drive/service-accounts#google_apis_console_project_service_accounts

Some issues with your code:

  • The ID of the service account should be in the form: <some-id>@developer.gserviceaccount.com (yes the email instead of the Client ID, I know it's weird)
  • You only set the principal when doing Google Apps domain Wide delegation but you can't do that on Gmail accounts of course, only on Google Apps accounts whose domain you have been granted access to by an administrator so in your case: don't set it.

Below is the code sample for OAuth 2.0 w/ Service accounts in Java.

Note: You'll also need to download the URL Shortener library.

/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL = "<some-id>@developer.gserviceaccount.com";

/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "/path/to/<public_key_fingerprint>-privatekey.p12";

/**
 * Build and returns a URL Shortner service object authorized with the service accounts.
 *
 * @return URL Shortner service object that is ready to make requests.
 */
public static Drive getDriveService() throws GeneralSecurityException, IOException, URISyntaxException {
  HttpTransport httpTransport = new NetHttpTransport();
  JacksonFactory jsonFactory = new JacksonFactory();
  GoogleCredential credential = new GoogleCredential.Builder()
      .setTransport(httpTransport)
      .setJsonFactory(jsonFactory)
      .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
      .setServiceAccountScopes(UrlshortenerScopes.URLSHORTENER)
      .setServiceAccountPrivateKeyFromP12File(
          new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
      .build();
  Urlshortener service = new Urlshortener.Builder(httpTransport, jsonFactory, null)
      .setHttpRequestInitializer(credential).build();
  return service;
}

这篇关于无法使用 Google API [google-oauth-java-client-1.12.0-beta] 获取服务帐户流程的令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆