8

我正在使用 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;
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();
  }
}

结果:

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)

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

4

1 回答 1

7

在 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,我知道这很奇怪)
  • 您只principal在执行Google Apps 域范围委托时设置,但您当然不能在 Gmail 帐户上执行此操作,只能在管理员已授予您访问权限的域的 Google Apps 帐户上设置,因此在您的情况下:不要设置它.

下面是 OAuth 2.0 的代码示例 w/Java 中的服务帐户。

注意:您还需要下载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;
}
于 2013-01-10T22:37:35.217 回答