无法使用 Google API [google-oauth-java-client-1.12.0-beta] 获取服务帐户流程的令牌

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14188239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 15:24:29  来源:igfitidea点击:

Unable to get token using Google APIs [google-oauth-java-client-1.12.0-beta] for service account flow

javaoauth-2.0google-oauthgoogle-url-shortener

提问by dan22

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

我正在使用 Google API(版本 google-oauth-java-client-1.12.0-beta)来获取 OAuth2 访问令牌,但返回“invalid_grant”。参考: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.Hymanson2.HymansonFactory;
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 = "[email protected]";
    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 HymansonFactory();
  }

  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.

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

回答by Nicolas Garnier

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.

在 Google 服务和 Google API 客户端库上使用服务帐户时,您不必自己创建签名和构建 JWT 令牌,因为有可直接使用的实用程序类,只需通过 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

然而,除了包含多种编程语言的详细解释和代码示例的 Google Drive 文档外,这并没有得到很好的记录。您应该阅读: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 principalwhen doing Google Apps domain Wide delegationbut 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.
  • 服务帐户的 ID 应采用以下形式:(<some-id>@developer.gserviceaccount.com是的电子邮件而不是客户 ID,我知道这很奇怪)
  • 您只能principal在进行Google Apps 域范围委托时设置.

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

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

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

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

/** 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();
  HymansonFactory jsonFactory = new HymansonFactory();
  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;
}