You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用服务账号调用Google Cloud Function遇权限问题求助

如何通过服务账号认证调用Google Cloud Function?

我需要通过服务账号调用Google Cloud Function,服务账号my-project-crendential已配置Cloud Functions-Invoker角色。编写了以下Java代码调用名为my-test-function的云函数:

ServiceAccountCredentials
            .fromStream(new FileInputStream(PATH != null ? PATH : "my-project-crendential.json"))

this.settings = CloudFunctionsServiceSettings.newBuilder()
            .setCredentialsProvider(FixedCredentialsProvider.create(this.credentials))
            .build();


try (CloudFunctionsServiceClient client = CloudFunctionsServiceClient.create(this.settings)) {
      final var functionNameWithLocation = String.format(
          "projects/%s/locations/%s/functions/%s",
          "my-project-id",
          "europe-west4",
          "my-test-function"
      );
      CallFunctionRequest request = CallFunctionRequest.newBuilder()
             .setName(functionNameWithLocation)
             .setData(body.encode())
             .build();
      CallFunctionResponse response = client.callFunction(request);

      log.atDebug().log("Function response received {} ...", response.getResult());
            
 } catch (Exception e) {
      log.atError().withThrowable(e).log("Create repository failed ...");
 }

该云函数的访问URL为https://europe-west4-my-project-id.cloudfunctions.net/my-test-function,但始终收到异常:

io.grpc.StatusRuntimeException: PERMISSION_DENIED: Permission denied on 'locations/europe-west4' (or it may not exist)

我确认区域存在且权限配置无误,使用的依赖为:

<dependency>
     <groupId>com.google.cloud</groupId>
     <artifactId>google-cloud-functions</artifactId>
     <version>2.35.0</version>
</dependency>

尝试1:获取OAuth Token调用API

根据反馈原API仅用于测试,服务账号无对应权限,于是尝试获取OAuth Token:

String serviceAccountKeyFile = path;//"/path/to/service-account-key.json";
    String projectId = "my-project-id";
    String location = "europe-west4";
    String functionName = "my-test-function";

    GoogleCredentials credentials = GoogleCredentials
            .fromStream(new FileInputStream(serviceAccountKeyFile))
            .createScoped("https://www.googleapis.com/auth/cloud-platform");

    String accessToken = credentials.refreshAccessToken().getTokenValue();

随后调用Get API:

String getApiUrl = String.format("https://cloudfunctions.googleapis.com/v2/projects/%s/locations/%s/functions/%s",
            projectId, location, functionName);
webClient.getAbs(getApiUrl)
            .bearerTokenAuthentication(identityToken)
            .putHeader("Authorization", "Bearer " + accessToken)
            .send(getResponse -> {
                if (getResponse.succeeded()) {
// getResponse does not contains HTTPStrigger.URL attribute
}});

响应中不包含HTTPStrigger.URL属性,且通过响应中的URL调用云函数时仍收到相同的“区域不存在”异常。


尝试2:参考官方示例使用ID Token调用

参考官方示例编写代码:

final var serviceAccountKeyFile = "/my/abs/path/to/my-project-secret-file.json";

    GoogleCredentials credentials = GoogleCredentials
            .fromStream(new FileInputStream(serviceAccountKeyFile))
            .createScoped("https://www.googleapis.com/auth/cloud-platform");

    final var audience = "https://europe-west4-my-project.cloudfunctions.net/my-function";

    if (!(credentials instanceof IdTokenProvider)) {
        throw new IllegalArgumentException("Credentials are not an instance of IdTokenProvider.");
    }
    IdTokenCredentials tokenCredential =
            IdTokenCredentials.newBuilder()
                    .setIdTokenProvider((IdTokenProvider) credentials)
                    .setTargetAudience(audience)
                    .build();

    GenericUrl genericUrl = new GenericUrl("https://europe-west4-my-project.cloudfunctions.net/my-function");
    HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
    HttpTransport transport = new NetHttpTransport();
    HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
    final var result = request.execute();
    System.out.println(result.parseAsString());

服务账号已拥有调用者角色,但仍收到异常:Your client does not have permission to get URL /my-function from this server.


解决方案

1. 明确正确的调用方式

CloudFunctionsServiceClient.callFunction是Cloud Functions v1 API的管理/测试接口,仅用于在Google Cloud内部触发函数,不适用于调用公开的HTTP触发云函数。HTTP触发的云函数需要直接调用其HTTPS端点,并携带ID Token(而非普通OAuth Access Token)进行认证。

2. 修正ID Token调用的核心错误

你的尝试2中存在两个关键错误:

  • Audience不匹配:audience必须完全与云函数的HTTPS URL一致,之前代码中使用的https://europe-west4-my-project.cloudfunctions.net/my-function与实际URLhttps://europe-west4-my-project-id.cloudfunctions.net/my-test-function存在项目ID和函数名的拼写错误。
  • 请求方法不匹配:多数云函数默认接受POST请求,若你的函数是POST触发,需将buildGetRequest改为buildPostRequest并传入请求体。

修正后的完整代码示例:

import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpCredentialsAdapter;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.StringContent;

import java.io.FileInputStream;

public class CloudFunctionInvoker {
    public static void main(String[] args) throws Exception {
        // 服务账号密钥文件路径
        final String serviceAccountKeyPath = "/my/abs/path/to/my-project-secret-file.json";
        // 云函数的完整HTTPS URL,确保完全一致
        final String functionEndpoint = "https://europe-west4-my-project-id.cloudfunctions.net/my-test-function";

        // 加载服务账号凭证
        GoogleCredentials credentials = GoogleCredentials
                .fromStream(new FileInputStream(serviceAccountKeyPath))
                .createScoped("https://www.googleapis.com/auth/cloud-platform");

        // 验证凭证支持ID Token生成
        if (!(credentials instanceof IdTokenProvider)) {
            throw new IllegalArgumentException("当前凭证不支持生成ID Token");
        }

        // 构建ID Token凭证,目标受众为云函数URL
        IdTokenCredentials idTokenCredential = IdTokenCredentials.newBuilder()
                .setIdTokenProvider((IdTokenProvider) credentials)
                .setTargetAudience(functionEndpoint)
                .build();

        // 创建HTTP传输和请求
        HttpTransport httpTransport = new NetHttpTransport();
        // 若函数为GET触发,替换为buildGetRequest
        HttpRequest request = httpTransport.createRequestFactory(new HttpCredentialsAdapter(idTokenCredential))
                .buildPostRequest(
                        new GenericUrl(functionEndpoint),
                        new StringContent("application/json", "{\"param\": \"test-data\"}") // 根据函数需求传入请求体
                );

        // 执行请求并输出结果
        var response = request.execute();
        System.out.println("函数响应:" + response.parseAsString());
    }
}

3. 验证服务账号权限

确保服务账号确实被授予roles/cloudfunctions.invoker角色,可通过gcloud命令重新绑定权限:

gcloud functions add-iam-policy-binding my-test-function \
  --region=europe-west4 \
  --member=serviceAccount:my-project-crendential@my-project-id.iam.gserviceaccount.com \
  --role=roles/cloudfunctions.invoker

4. 确保依赖兼容性

添加明确的Google Auth依赖,避免版本冲突:

<dependency>
    <groupId>com.google.auth</groupId>
    <artifactId>google-auth-library-oauth2-http</artifactId>
    <version>1.20.0</version>
</dependency>
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-functions</artifactId>
    <version>2.35.0</version>
</dependency>

内容的提问来源于stack exchange,提问作者Qeychon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:25:56