java - Google Cloud Platform - 云函数 API - 401 未经授权

标签 java rest google-cloud-platform google-cloud-functions

我正在努力使用 Java 通过 REST API 调用 GCP 云函数。

我为此执行的步骤是:

  • 创建一个具有“Cloud Functions Invoker”角色的服务帐户
  • 下载新创建的服务帐户的 JSON key 文件
  • 在我的代码中,使用以下方法获取访问 token :
private String getAuthToken() {
  File credentialsPath = new File(PATH_TO_JSON_KEY_FILE);

  GoogleCredentials credentials;
  try (FileInputStream serviceAccountStream = new FileInputStream(credentialsPath)) {
    credentials = ServiceAccountCredentials.fromStream(serviceAccountStream);
    return credentials
           .createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"))
           .refreshAccessToken()
           .getTokenValue();
  } catch (IOException e) {
    throw new RuntimeException("Action could not be performed");
  }
}
  • 使用创建的 token 执行 REST 调用:
public <Payload, Response> ResponseEntity<Response> callCloudFunction(
    String endpoint,
    Payload payload,
    Class<Response> klazz
) {
  RestTemplate restTemplate = new RestTemplate();
  HttpHeaders headers = new HttpHeaders();
  headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
  headers.setContentType(MediaType.APPLICATION_JSON);
  String url = gCloudUrl + endpoint;
  String token = getAuthToken();
  String payloadString = null;

  if (payload != null) {
    try {
      ObjectMapper objectMapper = new ObjectMapper();
      payloadString = objectMapper.writeValueAsString(payload);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
      throw new RuntimeException("Could not perform action");
    }
  }

  headers.add("Authorization", String.format("Bearer %s", token));
  HttpEntity<String> entity = new HttpEntity<>(payloadString, headers);

  return restTemplate.exchange(url, HttpMethod.POST, entity, klazz);
}

实现看起来不错,但作为响应,我收到 401 Unauthorized。

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

最佳答案

首先,同意,目前还不清楚......

然后,您必须知道(但又不清楚)您需要访问 token 来调用 Google Cloud API,但需要身份 token 来调用 IAP(例如在 App Engine 上)或私有(private) Cloud Function 和 Cloud Run。并且此身份 token 需要经过 Google 签名。

而且,正如代码中提到的,您的计算机上需要有一个服务帐户,但我建议您在 GCP 上避免这种情况,如果您使用默认身份验证则不需要(请参阅我的代码,在您的计算机上设置指向服务帐户 key 文件的 GOOGLE_APPLICATION_CREDENTIALS env var)。最好的方法是不在您的计算机上使用服务帐户 key 文件,但这还不可能(IMO 这是一个安全问题,我正在与 Google 讨论这个问题...)

无论如何,这里有一个可以在 Java 中运行的代码片段(文档中没有...)

  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)));

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

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

关于java - Google Cloud Platform - 云函数 API - 401 未经授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62863140/

相关文章:

Django Rest框架在没有 View 集的情况下执行自定义函数(远程过程调用)

github - 是否可以在 Google Cloud Composer 上安装 github 存储库

java - 当我一次使用 setcontentpane 时如何添加按钮?

java - JAX-RS:提供 "default"子资源定位器

java - 如何检查 REST 请求中是否包含不支持的参数?

google-cloud-platform - 如何使用 sourceIP 在公共(public) GKE 集群 pod 中使用云 NAT

firebase - Cloud Function 将数据添加到 Firestore 时出现权限错误

java - jar 文件位置 - Ear 文件还是服务器 lib 文件夹?

java - sublist(from,to).clear() 是否允许对 ArrayList 的清除部分进行垃圾回收?

Java:按钮数组 - 获取源代码