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

使用Firebase Admin SDK向FCM发送通知时遇401未授权错误

Firebase Admin SDK 401 Unauthorized 问题排查与解决

问题场景

  • 此前已基于Spring Boot + Firebase Admin SDK实现FCM通知发送,功能正常
  • 适配Firebase Emulator后恢复代码,出现401未授权错误,错误详情:

Caused by: com.google.api.client.http.HttpResponseException: 401 Unauthorized
POST https://fcm.googleapis.com/v1/projects/workmerk-eec65/messages:send
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}

  • 异常特征:Postman使用同一配置文件调用FCM接口正常,同事本地运行代码无问题,仅自身机器报错;已删除模拟器文件夹、检查全局环境变量,未发现明显问题

配置类代码

@Configuration
public class FirebaseConfig {

    @Value("${firebase.config}")
    private String firebaseConfigPath;

    @Value("${firebase.useEmulator:false}")
    private boolean useEmulator;

    @PostConstruct
    public void initialize() {
        try {
            InputStream serviceAccount = getClass().getClassLoader().getResourceAsStream(firebaseConfigPath);
            if (serviceAccount == null) {
                throw new RuntimeException("Firebase configuration file not found in classpath.");
            }

            FirebaseOptions.Builder optionsBuilder = FirebaseOptions.builder()
                    .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                    .setProjectId("workmerk-eec65");

//            if (useEmulator) {
//                // Emulator configuration
//                FirestoreOptions firestoreOptions = FirestoreOptions.newBuilder()
//                        .setEmulatorHost("localhost:8085")
//                        .build();
//                optionsBuilder.setFirestoreOptions(firestoreOptions)
//                        .setCredentials(new EmulatorCredentials());
//            }

            FirebaseOptions options = optionsBuilder.build();

            if (FirebaseApp.getApps().isEmpty()) {
                FirebaseApp.initializeApp(options);
            }

        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize Firebase", e);
        }
    }
}

排查与解决步骤

1. 清理本地Google凭据缓存

Firebase Admin SDK会缓存Google身份凭据,模拟器适配阶段的配置可能残留其中:

  • Linux/macOS:删除~/.config/gcloud/目录下的credentials和access_tokens文件夹,或直接清空整个gcloud目录
  • Windows:删除C:\Users\<你的用户名>\AppData\Roaming\gcloud\下的对应缓存文件夹
  • 重启Spring Boot项目,让SDK重新加载配置生成有效凭据

2. 清除Spring Boot构建与IDE缓存

本地开发环境的缓存可能保留旧的模拟器配置:

  • Maven项目执行mvn clean install,Gradle项目执行gradle clean build,彻底清理构建产物
  • 清除IDE缓存(如IDEA:File -> Invalidate Caches... -> Invalidate and Restart)
  • 确认application.properties/yml中firebase.useEmulator明确设置为false,且未被其他配置文件覆盖

3. 检查局部环境变量

全局环境变量无问题,但IDE运行配置可能残留模拟器相关变量:

  • 打开IDE的运行配置界面,检查是否存在FIREBASE_EMULATOR_HOST、GCLOUD_PROJECT等模拟器相关环境变量,如有则删除
  • 再次确认用户级环境变量中无Firebase模拟器相关配置

4. 验证凭据加载有效性

确保服务账号密钥文件正确加载:

  • 重新下载Firebase控制台的服务账号密钥文件,替换项目中的旧文件,确认文件路径与firebase.config配置一致
  • 在初始化代码中添加日志,验证凭据加载状态:
// 在initialize方法中添加日志
System.out.println("Firebase config path: " + firebaseConfigPath);
GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccount);
System.out.println("Loaded credential type: " + credentials.getAuthenticationType());

5. 重置FirebaseApp实例

旧的FirebaseApp实例可能残留模拟器配置,需先销毁再初始化:

// 在initialize方法开头添加
for (FirebaseApp app : FirebaseApp.getApps()) {
    app.delete();
}

之后再执行原有的初始化逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 14:47:29