我正在尝试使用服务帐户方法访问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> datasets = datasetList.getDatasets();
System.out.println("Available datasetsn----------------");
for (Datasets dataset : datasets) {
System.out.format("%sn", dataset.getDatasetReference().getDatasetId());
}
}
但它会引发以下异常:
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)
在此行上触发异常:
DatasetList datasetList = datasetRequest.execute();
我从 Google 的 API 控制台从该部分的第二行获取帐户 ID,如下所示:
Client ID: XXXXX.apps.googleusercontent.com
Email address: XXXXX@developer.gserviceaccount.com
我错过了什么?
尤里卡!Eric 和 Michael 的代码都运行良好。
问题中发布的错误可以通过错误地设置客户端计算机上的时间来重现。幸运的是,可以通过在客户端计算机上正确设置时间来解决。
注意:为了它的价值,我使用"互联网时间设置"对话框中的"立即更新"按钮在Windows 7盒子上同步了时间。我想这应该是相当白痴的防...但我想我打败了系统。它校正了秒数,但将机器关闭了整整一分钟。之后,BigQuery 调用失败。在我手动更改时间后它成功了。
我们在 Java 库中的错误处理代码需要改进一点!
看起来用于请求 OAuth 访问令牌的已签名 JWT 失败。 您可以通过启用上面提到的日志来查看@MichaelManoochehri这一点。
我认为只有几件事可能导致此失败:
- 签名无效(使用错误的密钥)
- 服务帐户的电子邮件地址无效(我认为已被排除)
- 用于生成已签名 Blob 的日期/时间戳无效(问题日期和到期日期)
- 无效范围(我认为已被排除)
您应该检查您的日期/时间是否在服务器上使用正确的时区正确设置 - 同步到 NTP。 您可以使用 time.gov 查看美国官方原子钟时间。
编辑:我在下面给出的答案与使用Google App Engine服务帐户有关 - 留在这里供参考。
仔细检查您是否已将服务帐户地址作为所有者添加到项目的团队页面。
我建议使用 AppIdentityCredential 类来处理服务帐户身份验证。下面是演示这一点的小片段,我将在 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<TRANSPORT> 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("%sn", datasetList.toPrettyString());
}