【问题标题】:Invoke a Google Cloud Run from java从 java 调用 Google Cloud Run
【发布时间】:2020-07-14 20:53:47
【问题描述】:

我想从外部应用调用 Cloud Run。外部应用程序是用 Kotlin (java) 编写的,并在 JDK 11 JVM 上运行。它使用服务帐户进行身份验证

ServiceAccountCredentials.fromStream(serviceAccount).createScoped("https://www.googleapis.com/auth/cloud-platform")

服务帐户所在的位置是一个有效的 JSON 文件。我已经测试了在谷歌云控制台中为服务帐户调用云运行的权限。

我还没有找到官方 API,所以我使用 google 的 GoogleNetHttpTransport 来运行 HTTP 发布请求。 为了调用云运行,我尝试使用 HttpCredentialsAdapter

transport.createRequestFactory(HttpCredentialsAdapter(googleCredentials))
   .buildPostRequest(GenericUrl(url), JsonHttpContent(JacksonFactory(), data))

我已尝试直接使用访问令牌

val headers = HttpHeaders()
googleCredentials.refreshIfExpired()
headers.authorization = "Bearer " + googleCredentials.accessToken.tokenValue
postRequest.headers = headers

我已经尝试使用 jet 服务帐户并使用 jwt 请求元数据

val headers = HttpHeaders()
jwtCredentials = ServiceAccountJwtAccessCredentials.fromStream(serviceAccount)
jwtCredentials.getRequestMetadata(URI(url)).forEach {
    headers.set(it.key, it.value)
}
postRequest.headers = headers

在所有这些情况下,它都会返回此错误

[20:35:00 WARN]: Exception in thread "TestThread" com.google.api.client.http.HttpResponseException: 401 Unauthorized
[20:35:00 WARN]:    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1113)
[20:35:00 WARN]:    at ***.CloudRun$invoke$3.invokeSuspend(CloudRun.kt:34)
[20:35:00 WARN]:    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[20:35:00 WARN]:    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[20:35:00 WARN]:    at java.base/java.lang.Thread.run(Thread.java:834)

提前感谢您的支持

【问题讨论】:

  • “调用云运行”是什么意思? Cloud Run 是一个用于托管 REST / HTTP 应用程序的平台/环境。您创建一个监听 $PORT 并响应 HTTP 请求的应用程序。您的意思是调用托管在容器中的自定义 REST 应用程序吗?如果是,该应用程序的性质是什么?
  • 我想向另一个在云运行容器中运行的 REST 应用程序发布请求。 cloud run用于各种任务,用node js编写。

标签: java google-cloud-run


【解决方案1】:

我花时间在 OAuth2 Java 库上,问题如下。 ServiceAccountCredentials 类添加一个AccessToken 作为授权,ServiceAccountJwtAccessCredentials 类添加一个自签名 identity token

我会更深入,但现在你可以手动设置标题

        String myUri = "https://.....";

        Credentials credentials =  ServiceAccountCredentials
                .fromStream(resourceLoader.getResource("classpath:./key.json")
                        .getInputStream()).createScoped("https://www.googleapis.com/auth/cloud-platform");

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

        HttpRequestFactory factory = new NetHttpTransport().createRequestFactory();
        HttpRequest request = factory.buildGetRequest(new GenericUrl(myUri));
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization","Bearer " + token);
        request.setHeaders(headers);
        HttpResponse httpResponse = request.execute();
        System.out.println(CharStreams.toString(new InputStreamReader(httpResponse.getContent(), Charsets.UTF_8)));

这些部分在这里,但不能一起工作。

编辑

在 GitHub 上进行了一些交流后,谷歌为我提供了解决方案。现在,标头会自动配置有签名的 id 令牌

        Credentials credentials =  ServiceAccountCredentials
                .fromStream(resourceLoader.getResource("classpath:./key.json")
                        .getInputStream()).createScoped("https://www.googleapis.com/auth/cloud-platform");


        IdTokenCredentials idTokenCredentials = IdTokenCredentials.newBuilder()
                .setIdTokenProvider((ServiceAccountCredentials)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)));

【讨论】:

  • 所以我想这基本上就是我所说的,但你知道 Java。 :)
  • @Gabber235 我用 Java 内置模式更新了我的答案!无需手动标题!
【解决方案2】:

我不太了解 Java SDK,但我想我发现了错误:

headers.authorization = "Bearer " + googleCredentials.accessToken.tokenValue
                                                      ^^^^^^^^^^^

您不应向 Cloud Run 应用程序发送 GCP“访问令牌”(例如 https://*.run.app)。该令牌用于调用 Google 的 API;而 Cloud Run 应用并非如此。

相反,您应该发送一个“身份令牌”,这基本上表明您拥有服务帐户,而无需向被调用的服务授予任何权限(使用该令牌调用 GCP API)。

您不能自己在本地签署“身份令牌”,您需要调用端点。

本页对此进行了解释:https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-jwt

【讨论】:

    猜你喜欢
    • 2020-05-29
    • 2021-12-11
    • 2020-08-19
    • 2020-11-05
    • 1970-01-01
    • 2020-05-27
    • 2020-05-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多