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

Java Firebase认证在Eclipse中正常,导出JAR后失效问题排查

核心问题:同一环境下IDE运行正常,导出JAR后Firebase认证失败?

问题背景

开发了一款供内部文档使用的小型Java工具,负责向Google Firestore数据库上传数据,供另一款内部Web应用读取。因场景风险较低,采用服务账号完成Firebase登录:从云控制台下载JSON认证文件,放置在执行路径供代码读取。

异常现象

在Eclipse开发环境中运行完全正常:数据可无错误上传,且能被Web应用正常读取。但将工具编译导出为可运行JAR后,数据上传失败,栈追踪信息如下:

com.google.api.gax.rpc.UnavailableException: io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata
[...]
Caused by: io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata
[...]
Caused by: com.google.auth.oauth2.GoogleAuthException: Error getting access token for service account: Received fatal alert: handshake_failure, iss: [我的服务账号邮箱]

已排查情况

  • 排除系统时间问题,测试设备时间均正常,同一开发机器上也出现该现象
  • 使用同一OpenJDK 16,IDE内运行正常,但运行导出的JAR时无法登录Firebase
  • 检查Eclipse运行命令行,除大量显式依赖外无特殊配置,IDE运行配置中无缺失的环境变量或命令行参数

相关代码片段

认证代码(IDE和JAR中均无执行错误)

private static Firestore getFirestoreInstance() {
    if (firestoreDbInstance != null) {
       return firestoreDbInstance;
    }
    InputStream serviceAccountFile;
    try {
        serviceAccountFile = new FileInputStream(/*path to the credentials file*/);
    } catch (FileNotFoundException e) { 
        writeExceptionToLog(e);
        return null;
    }
    GoogleCredentials credentials;
    try {
        credentials = GoogleCredentials.fromStream(serviceAccountFile);

        FirebaseOptions options = FirebaseOptions.builder().setCredentials(credentials)
                .setProjectId(APIConstants.projectId).build();

        FirebaseApp.initializeApp(options);

        firestoreDbInstance = FirestoreClient.getFirestore();

        return firestoreDbInstance;
    } catch (IOException e) {
        writeToLog("Error getting firestore instance");
        writeExceptionToLog(e);
    }
    return null;
}

数据上传方法(错误发生在onFailure回调,异常指向认证问题)

Firestore db = getFirestoreInstance();

UUID uuid = UUID.randomUUID();

DocumentReference docRef = db.collection(APIConstants.pendingRequestsCollection).document(uuid.toString());

Map<String, Object> data = new HashMap<>();
[...adding JSON data...]
ApiFuture<WriteResult> result = docRef.set(data);

ApiFutures.addCallback(result, new ApiFutureCallback<WriteResult>() {
    @Override
    public void onFailure(Throwable throwable) {
        [...showing dialog with error...]
        writeExceptionToLog(throwable);
    }

    @Override
    public void onSuccess(WriteResult writeResult) {
        writeToLog("Upload successful: " + uuid.toString());
    }
}, Runnable::run);

相关版本

  • Eclipse 2023-12 (4.30.0)
  • Maven依赖:com.google.firebase:firebase-admin:9.2.0

核心疑问

同一JDK、同一认证文件、同一依赖包下,为何IDE中运行正常但导出JAR后上传失败?


可能的原因及解决方案

  1. JAR打包时依赖缺失或冲突
    Eclipse运行时会自动管理依赖,但导出普通可运行JAR时,可能未包含Firebase/GRPC相关的依赖包,或出现版本冲突。

    • 解决办法:使用Maven的maven-shade-plugin或maven-assembly-plugin打包成包含所有依赖的胖JAR,确保GRPC、Google Auth等必要依赖被正确包含。
  2. 认证文件路径问题
    代码中使用FileInputStream读取文件,IDE执行路径与JAR运行路径可能不同,导致JAR运行时无法正确找到认证文件(虽未抛出FileNotFoundException,仍需确认路径有效性)。

    • 解决办法:
      • 将认证文件放在JAR同目录下,使用相对路径读取;
      • 或把认证文件打包进JAR,改用getClass().getResourceAsStream("/credentials.json")方式读取,避免路径依赖。
  3. TLS/SSL握手失败
    栈追踪中的handshake_failure说明SSL握手失败,可能是JAR运行时的JDK缺少Google相关根证书,或GRPC的SSL配置在打包后异常。

    • 解决办法:
      • 检查JDK的cacerts证书库是否包含所需根证书,必要时导入缺失证书;
      • 添加JVM参数-Djavax.net.debug=ssl调试SSL握手过程,定位具体失败原因。
  4. Firebase App重复初始化
    JAR运行时的类加载机制与IDE不同,可能导致FirebaseApp.initializeApp(options)被多次调用,引发认证异常。

    • 解决办法:初始化前先检查是否已有实例:
      if (FirebaseApp.getApps().isEmpty()) {
          FirebaseApp.initializeApp(options);
      }
      

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:45:02