Java 谷歌云平台-云功能API-401未经授权

Java 谷歌云平台-云功能API-401未经授权,java,rest,google-cloud-platform,google-cloud-functions,Java,Rest,Google Cloud Platform,Google Cloud Functions,我正在努力使用Java通过RESTAPI调用GCP云函数 我执行的步骤是: 创建一个角色为“云函数调用器”的服务帐户 下载新创建的服务帐户的JSON密钥文件 在我的代码中,使用以下方法获取访问令牌: 使用创建的令牌执行REST调用: public ResponseEntity callcloud函数( 字符串端点, 有效载荷, 克拉兹班 ) { RestTemplate RestTemplate=新RestTemplate(); HttpHeaders=新的HttpHeaders();

我正在努力使用Java通过RESTAPI调用GCP云函数

我执行的步骤是:

  • 创建一个角色为“云函数调用器”的服务帐户
  • 下载新创建的服务帐户的JSON密钥文件
  • 在我的代码中,使用以下方法获取访问令牌:
  • 使用创建的令牌执行REST调用:
public ResponseEntity callcloud函数(
字符串端点,
有效载荷,
克拉兹班
) {
RestTemplate RestTemplate=新RestTemplate();
HttpHeaders=新的HttpHeaders();
setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
字符串url=gCloudUrl+端点;
字符串标记=getAuthToken();
字符串payloadString=null;
如果(有效负载!=null){
试一试{
ObjectMapper ObjectMapper=新的ObjectMapper();
payloadString=objectMapper.writeValueAsString(有效载荷);
}捕获(JsonProcessingException e){
System.out.println(e.getMessage());
抛出新的RuntimeException(“无法执行操作”);
}
}
headers.add(“授权”,String.format(“承载%s”,令牌));
HttpEntity=新的HttpEntity(payloadString,标题);
返回restemplate.exchange(url、HttpMethod.POST、entity、klazz);
}
实现看起来不错,但作为回应,我得到了401授权


不幸的是,GCP文档并没有真正的帮助。我想我已经搜索了所有可能的地方。

首先,我同意,不清楚

然后,您必须知道(现在还不清楚)您需要一个访问令牌来调用Google Cloud API,但需要一个身份令牌来调用IAP(例如在App Engine上)或私有云函数和云运行。这个身份令牌需要谷歌签名

而且,正如代码中提到的,您需要在您的计算机上拥有一个服务帐户,但我建议您在GCP上避免这种情况,如果您使用默认身份验证(请参阅我的代码,在您的计算机上设置指向服务帐户密钥文件的
GOOGLE\u应用程序\u凭据
env var)。最好的方法是不要在你的计算机上使用服务帐户密钥文件,但这还不可能(在我看来这是一个安全问题,我正在就此与谷歌讨论…)

无论如何,这里有一个在Java中工作的代码片段(文档中没有任何地方…)

注意:如果您想继续使用RestTemplate对象并手动设置您的令牌,您可以这样生成它

  String token = ((IdTokenProvider) credentials).idTokenWithAudience(myUri, Collections.EMPTY_LIST).getTokenValue();
  System.out.println(token);

首先,我同意,现在还不清楚

然后,您必须知道(现在还不清楚)您需要一个访问令牌来调用Google Cloud API,但需要一个身份令牌来调用IAP(例如在App Engine上)或私有云函数和云运行。这个身份令牌需要谷歌签名

而且,正如代码中提到的,您需要在您的计算机上拥有一个服务帐户,但我建议您在GCP上避免这种情况,如果您使用默认身份验证(请参阅我的代码,在您的计算机上设置指向服务帐户密钥文件的
GOOGLE\u应用程序\u凭据
env var)。最好的方法是不要在你的计算机上使用服务帐户密钥文件,但这还不可能(在我看来这是一个安全问题,我正在就此与谷歌讨论…)

无论如何,这里有一个在Java中工作的代码片段(文档中没有任何地方…)

注意:如果您想继续使用RestTemplate对象并手动设置您的令牌,您可以这样生成它

  String token = ((IdTokenProvider) credentials).idTokenWithAudience(myUri, Collections.EMPTY_LIST).getTokenValue();
  System.out.println(token);

我不敢相信这一点,但它工作起来很有魅力:D我的IDE只警告
IdTokenProvider
被标记为不稳定(由于
@Beta
注释),但老实说,我不在乎;)关于这个话题,我做了几十次尝试。你的帮助是无价之宝:)@guillame,谢谢你成为英雄,让谷歌认证的疯狂不再神秘。然而,在各种StackOverflow文章中,我没有看到关于如何在dev/test/GCP环境之间切换时使其可互操作和无缝的好建议。有什么建议吗?在Java或其他语言中,关键是依赖于您获得的ADC(应用程序默认凭据),如
GoogleCredentials.getApplicationDefault()
。因此,应用程序依赖于环境配置。在dev环境中,您在dev项目上。您使用专用于项目的服务帐户进行部署,应用程序会自动使用它,因为它属于环境上下文。对于其他环境也是如此。如果您有特定的用例或阻止程序,请随意分享(在新问题中,并将链接粘贴到此处!)问题在于
gcloud auth application default login
启动浏览器,强制您选择谷歌用户帐户。当设置为这种方式时,
GoogleCredentials.getApplicationDefault()
方法返回
com.google.auth.oauth2.UserCredentials
的实例,而不是所需的
IdTokenProvider
。因此,本地似乎需要设置环境变量或服务帐户JSON文件的路径。我可以通过手动将一个服务帐户文件复制到一个名为
application\u default\u credentials.json的文件中来“欺骗”它,但这似乎有点令人讨厌。。。我知道。。。我将解决这个问题:您可以对这个合并请求进行upvote或注释,以加快其验证速度!我不知道为什么默认情况下它不在auth库中。任何语言都可以通过用户凭证生成有效的ID-TOKEN,尽管gcloud SDK可以(gcloud auth print identity TOKEN),但我不敢相信,它的工作原理就像一个符咒:D我的IDE只警告说,
IdTokenProvider
被标记为不稳定(由于
@Beta
注释),但老实说,我不在乎;)关于这个话题,我做了几十次尝试。你的
  String myUri = "https://path/to/url";
  // You can use here your service account key file. But, on GCP you don't require a service account key file.
  // However, on your computer, you require one because you need and identity token and you can generate it with your user account (long story... I'm still in discussion with Google about this point...)
  Credentials credentials = GoogleCredentials.getApplicationDefault().createScoped("https://www.googleapis.com/auth/cloud-platform");
  IdTokenCredentials idTokenCredentials = IdTokenCredentials.newBuilder()
    .setIdTokenProvider((IdTokenProvider) credentials)
    .setTargetAudience(myUri).build();

  HttpRequestFactory factory = new NetHttpTransport().createRequestFactory(new HttpCredentialsAdapter(idTokenCredentials));

  HttpRequest request = factory.buildGetRequest(new GenericUrl(myUri));
  HttpResponse httpResponse = request.execute();
  System.out.println(CharStreams.toString(new InputStreamReader(httpResponse.getContent(), Charsets.UTF_8)));
  String token = ((IdTokenProvider) credentials).idTokenWithAudience(myUri, Collections.EMPTY_LIST).getTokenValue();
  System.out.println(token);