谷歌云平台 - 云函数 API - 401 未经授权 [英] Google Cloud Platform - cloud functions API - 401 Unauthorized

查看:71
本文介绍了谷歌云平台 - 云函数 API - 401 未经授权的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm struggling with invoking GCP cloud functions via REST API using Java.

我执行的步骤是:

  • 创建一个具有Cloud Functions Invoker"角色的服务帐户
  • 为新创建的服务帐户下载 JSON 密钥文件
  • 在我的代码中,使用以下方法获取访问令牌:
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");
  }
}

  • 使用创建的令牌执行 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.

    The implementation looks fine, but in response I'm getting 401 Unauthorized.

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

    Unfortunately, GCP documentation is not really helpful. I think I've searched through all the possible places.

    推荐答案

    首先同意,不清楚...

    First of all, agree, it's not clear...

    然后,您必须知道(现在还不清楚)您需要访问令牌来调用 Google Cloud API,但需要身份令牌来调用 IAP(例如在 App Engine 上)或私有 Cloud Function 和 Cloud Run.并且此身份令牌需要由 Google 签名.

    Then, you have to know (and it's not clear again) that you need an access token to call Google Cloud API, but and identity token to call IAP (on App Engine for example) or private Cloud Function and Cloud Run. And this identity token need to be signed by Google.

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

    And, as mentioned in the code, you need to have a service account on your computer, but I recommend you to avoid this on GCP, it's not required if you use default authentication (see my code, on your computer set the GOOGLE_APPLICATION_CREDENTIALS env var that points to the service account key file). The best way is to not use service account key file on your computer also, but it's not yet possible (that is a security issue IMO, and I'm discussing with Google on this...)

    无论如何,这里有一个适用于 Java 的代码片段(文档中没有......)

    Anyway, here a code snippet which works in Java (nowhere in the documentation...)

      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 对象并手动设置您的令牌,您可以像这样生成它

    NOTE If you want to continue to use RestTemplate object and set manually your token, you can generate it like this

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

    这篇关于谷歌云平台 - 云函数 API - 401 未经授权的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆