使用Firebase Admin SDK向FCM发送通知时遇401未授权错误
问题场景
- 此前已基于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
相关产品推荐
相关产品推荐

