BigQuery和OAuth2

我正在尝试使用服务帐户方法访问Google BigQuery。 我的代码如下:

private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport(); private static final JsonFactory JSON_FACTORY = new JacksonFactory(); GoogleCredential credentials = new GoogleCredential.Builder() .setTransport(HTTP_TRANSPORT) .setJsonFactory(JSON_FACTORY) .setServiceAccountId("XXXXX@developer.gserviceaccount.com") .setServiceAccountScopes(BigqueryScopes.BIGQUERY) .setServiceAccountPrivateKeyFromP12File( new File("PATH-TO-privatekey.p12")) .build(); Bigquery bigquery = Bigquery.builder(HTTP_TRANSPORT, JSON_FACTORY).setHttpRequestInitializer(credentials) .build(); com.google.api.services.bigquery.Bigquery.Datasets.List datasetRequest = bigquery.datasets().list( "PROJECT_ID"); DatasetList datasetList = datasetRequest.execute(); if (datasetList.getDatasets() != null) { java.util.List datasets = datasetList.getDatasets(); System.out.println("Available datasets\n----------------"); for (Datasets dataset : datasets) { System.out.format("%s\n", dataset.getDatasetReference().getDatasetId()); } } 

但它抛出以下exception:

 Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized { "code" : 401, "errors" : [ { "domain" : "global", "location" : "Authorization", "locationType" : "header", "message" : "Authorization required", "reason" : "required" } ], "message" : "Authorization required" } at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:159) at com.google.api.client.googleapis.json.GoogleJsonResponseException.execute(GoogleJsonResponseException.java:187) at com.google.api.client.googleapis.services.GoogleClient.executeUnparsed(GoogleClient.java:115) at com.google.api.client.http.json.JsonHttpRequest.executeUnparsed(JsonHttpRequest.java:112) at com.google.api.services.bigquery.Bigquery$Datasets$List.execute(Bigquery.java:979) 

在此行上触发了exception:

  DatasetList datasetList = datasetRequest.execute(); 

我从第二行看到来自Google API控制台的帐户ID,如下所示:

  Client ID: XXXXX.apps.googleusercontent.com Email address: XXXXX@developer.gserviceaccount.com 

我错过了什么?

找到了! 埃里克和迈克尔的代码都运作良好。

通过在客户端计算机上错误地设置时间,可以重现问题中发布的错误。 幸运的是, 它可以通过正确设置客户端计算机上的时间来解决

注意:对于它的价值,我使用“Internet时间设置”对话框中的“立即更新”按钮在Windows 7框上同步时间。 我认为应该是非常白痴的…但我想我打败了系统。 它修正了秒数,但机器关闭了一分钟。 之后,BigQuery调用失败了。 手动更改时间后,它成功了。

我们在Java库中的error handling代码需要稍微改进一下!

看起来用于请求OAuth访问令牌的签名JWT失败。 你可以通过启用上面提到的@MichaelManoochehri的日志来看到这一点。

我认为只有少数几件事可能导致这种失败:

  1. 签名无效(使用错误的密钥)
  2. 服务帐户的电子邮件地址无效(我认为已被排除)
  3. 用于生成签名blob的日期/时间戳无效(发布日期和到期日期)
  4. 范围无效(我认为已被排除)

您应该使用适当的时区检查您的日期/时间是否正确设置 – 与NTP同步。 您可以使用time.gov查看美国官方primefaces钟时间。

编辑:我在下面给出的答案与使用Google App Engine服务帐户相关 – 请留待此处参考。

仔细检查您是否已将服务帐户地址作为所有者添加到项目的团队页面中。

我建议使用AppIdentityCredential类来处理服务帐户身份validation。 这是一个演示此内容的小片段,我将在BigQuery API开发者页面上添加有关此内容的其他文档。

此外,请确保您使用的是最新版本的Google Java API客户端(截至今天,它的版本为“v2-rev5-1.5.0-beta”)。

 import java.io.IOException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.api.client.googleapis.extensions.appengine.auth.oauth2.AppIdentityCredential; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.http.json.JsonHttpRequest; import com.google.api.client.http.json.JsonHttpRequestInitializer; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson.JacksonFactory; import com.google.api.services.bigquery.Bigquery; import com.google.api.services.bigquery.BigqueryRequest; @SuppressWarnings("serial") public class Bigquery_service_accounts_demoServlet extends HttpServlet { // ENTER YOUR PROJECT ID HERE private static final String PROJECT_ID = ""; private static final HttpTransport TRANSPORT = new NetHttpTransport(); private static final JsonFactory JSON_FACTORY = new JacksonFactory(); private static final String BIGQUERY_SCOPE = "https://www.googleapis.com/auth/bigquery"; AppIdentityCredential credential = new AppIdentityCredential(BIGQUERY_SCOPE); Bigquery bigquery = Bigquery.builder(TRANSPORT,JSON_FACTORY) .setHttpRequestInitializer(credential) .setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() { public void initialize(JsonHttpRequest request) { BigqueryRequest bigqueryRequest = (BigqueryRequest) request; bigqueryRequest.setPrettyPrint(true); } }).build(); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.getWriter().println(bigquery.datasets() .list(PROJECT_ID) .execute().toString()); } } 

以下是完整的参考片段供参考:

 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.jackson.JacksonFactory; import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.services.bigquery.Bigquery; import com.google.api.services.bigquery.Bigquery.Datasets; import com.google.api.services.bigquery.model.DatasetList; import java.io.File; import java.io.IOException; import java.security.GeneralSecurityException; public class BigQueryJavaServiceAccount { private static final String SCOPE = "https://www.googleapis.com/auth/bigquery"; private static final HttpTransport TRANSPORT = new NetHttpTransport(); private static final JsonFactory JSON_FACTORY = new JacksonFactory(); public static void main(String[] args) throws IOException, GeneralSecurityException { GoogleCredential credential = new GoogleCredential.Builder().setTransport(TRANSPORT) .setJsonFactory(JSON_FACTORY) .setServiceAccountId("XXXXXXX@developer.gserviceaccount.com") .setServiceAccountScopes(SCOPE) .setServiceAccountPrivateKeyFromP12File(new File("my_file.p12")) .build(); Bigquery bigquery = Bigquery.builder(TRANSPORT, JSON_FACTORY) .setApplicationName("Google-BigQuery-App/1.0") .setHttpRequestInitializer(credential).build(); Datasets.List datasetRequest = bigquery.datasets().list("publicdata"); DatasetList datasetList = datasetRequest.execute(); System.out.format("%s\n", datasetList.toPrettyString()); }