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

Google Pub/Sub Java代码调用报PERMISSION_DENIED错误求助

解决Google Pub/Sub Java客户端PERMISSION_DENIED异常(命令行正常,代码报错)

我之前遇到过几乎一模一样的问题——命令行用gcloud操作Pub/Sub完全正常,但Java代码死活抛出权限拒绝,哪怕服务账号已经是Project Owner权限。下面是几个最值得优先排查的方向:

1. 确认IntelliJ是否正确读取了GOOGLE_APPLICATION_CREDENTIALS环境变量

OSX下有个很容易踩的坑:如果是从Launchpad/Spotlight启动IntelliJ,它不会继承终端(比如zsh/bash)里配置的环境变量。这是最常见的根源。

  • 验证方法:在代码开头加一行打印,看看环境变量是否能被读取到:

    System.out.println("GOOGLE_APPLICATION_CREDENTIALS: " + System.getenv("GOOGLE_APPLICATION_CREDENTIALS"));
    

    如果输出是null,说明IntelliJ根本没拿到这个变量。

  • 解决方法:
    打开Run → Edit Configurations,选中你的运行配置,找到Environment Variables选项,点击加号手动添加:

    • 变量名:GOOGLE_APPLICATION_CREDENTIALS
    • 变量值:你的服务账号密钥文件的绝对路径(比如/Users/yourname/Documents/service-account-key.json)

2. 手动指定项目ID和凭证,排除自动加载问题

有时候ServiceOptions.getDefaultProjectId()可能会获取到gcloud的默认项目(而非服务账号关联的项目,虽然你说调试显示正确,但可以彻底排除这个可能性),或者自动加载凭证失败。可以直接手动指定:

修改你的代码,硬编码项目ID并手动加载凭证:

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.pubsub.v1.TopicAdminClient;
import com.google.cloud.pubsub.v1.TopicAdminSettings;
import com.google.pubsub.v1.ProjectTopicName;
import java.io.FileInputStream;

public class CreateTopicExample {
    public static void main(String... args) throws Exception {
        // 手动指定项目ID
        String projectId = "your-actual-project-id";
        String topicId = args[0];
        ProjectTopicName topic = ProjectTopicName.of(projectId, topicId);

        // 手动加载服务账号凭证
        GoogleCredentials credentials = GoogleCredentials.fromStream(
            new FileInputStream("/path/to/your/service-account-key.json")
        );
        TopicAdminSettings settings = TopicAdminSettings.newBuilder()
            .setCredentialsProvider(() -> credentials)
            .build();

        try (TopicAdminClient topicAdminClient = TopicAdminClient.create(settings)) {
            topicAdminClient.createTopic(topic);
            System.out.printf("Topic %s:%s created.%n", topic.getProject(), topic.getTopic());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如果这段代码能成功运行,说明问题出在自动加载机制上,回到第一个点解决环境变量的加载问题即可。

3. 检查Maven依赖的客户端版本

旧版本的Google Cloud客户端库可能存在认证相关的bug。确保你的pom.xml里使用的是最新稳定版的Pub/Sub依赖:

<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-pubsub</artifactId>
    <version>1.123.10</version> <!-- 可根据官方文档更新到最新版 -->
</dependency>

执行mvn clean install刷新依赖后再测试。

4. 确认服务账号权限无组织级限制

虽然你的服务账号是Project Owner,但有些企业组织会设置组织政策限制(比如禁用Pub/Sub API,或者限制服务账号的IAM权限)。可以:

  • 登录Google Cloud控制台,进入IAM页面,确认该服务账号的Owner权限已生效(没有被删除或禁用)。
  • 进入API库页面,确认Cloud Pub/Sub API已启用。

原始问题详情

我尝试按照Google Pub/Sub官方快速入门指南使用Java库开发,环境为IntelliJ+Maven+OSX+Java8。已创建拥有Project Owner权限的服务账号,安装Gcloud SDK并配置GOOGLE_APPLICATIONS_CREDENTIALS环境变量,命令行下可正常创建主题、发布消息等操作,但运行示例代码时抛出PERMISSION_DENIED异常。调试显示projectId已正确关联服务账号对应的项目,且控制台确认该服务账号拥有Project:Owner权限。

示例代码:

import com.google.api.gax.rpc.ApiException; 
import com.google.cloud.ServiceOptions; 
import com.google.cloud.pubsub.v1.TopicAdminClient; 
import com.google.pubsub.v1.ProjectTopicName; 

public class CreateTopicExample { 
    /** 
     * Create a topic. 
     * 
     * @param args topicId 
     * @throws Exception exception thrown if operation is unsuccessful 
     */ 
    public static void main(String... args) throws Exception { 
        // Your Google Cloud Platform project ID 
        String projectId = ServiceOptions.getDefaultProjectId(); 
        // Your topic ID, eg. "my-topic" 
        String topicId = args[0]; 

        // Create a new topic 
        ProjectTopicName topic = ProjectTopicName.of(projectId, topicId); 
        try (TopicAdminClient topicAdminClient = TopicAdminClient.create()) { 
            topicAdminClient.createTopic(topic); 
        } catch (ApiException e) { 
            // example : code = ALREADY_EXISTS(409) implies topic already exists 
            System.out.print(e.getStatusCode().getCode()); 
            System.out.print(e.isRetryable()); 
        } 
        System.out.printf("Topic %s:%s created.%n", topic.getProject(), topic.getTopic()); 
    } 
} 

完整堆栈信息:

com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException: PERMISSION_DENIED: User not authorized to perform this action. 
    at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:55) 
    at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:72) 
    at com.google.api.gax.grpc.GrpcApiExceptionFactory.create(GrpcApiExceptionFactory.java:60) 
    at com.google.api.gax.grpc.GrpcExceptionCallable$ExceptionTransformingFuture.onFailure(GrpcExceptionCallable.java:95) 
    at com.google.api.core.ApiFutures$1.onFailure(ApiFutures.java:61) 
    at com.google.common.util.concurrent.Futures$4.run(Futures.java:1123) 
    at com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:435) 
    at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:900) 
    at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:811) 
    at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:675) 
    at io.grpc.stub.ClientCalls$GrpcFuture.setException(ClientCalls.java:492) 
    at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:467) 
    at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:41) 
    at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:684) 
    at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:41) 
    at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:391) 
    at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:475) 
    at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:63) 
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:557) 
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$600(ClientCallImpl.java:478) 
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:590) 
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) 
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: io.grpc.StatusRuntimeException: PERMISSION_DENIED: User not authorized to perform this action. 
    at io.grpc.Status.asRuntimeException(Status.java:526) 
    ... 19 more 

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:24:11