我正在努力使用 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/