FCM发送数据消息一段时间后返回401错误求助
排查FCM REST API返回401错误的问题
从你的描述和代码片段来看,问题的核心在于AccessToken的过期检查逻辑缺失,导致运行一段时间后使用了过期的token发送请求,触发FCM的401 Unauthorized错误。下面具体分析并给出修复方案:
1. 问题根源:未检查AccessToken的有效期
你的getAccessToken()方法只在googleCredentials.getAccessToken()==null时才触发刷新,但Google生成的AccessToken默认有1小时(3600秒)的有效期。当token不为null但已经过期时,你仍然会使用这个失效的token发起请求,FCM自然会返回401错误。
2. 修复AccessToken刷新逻辑
修改getAccessToken()方法,增加对token过期时间的检查,确保每次发送请求前使用的都是有效的token:
public static synchronized String getAccessToken() { AccessToken currentToken = googleCredentials.getAccessToken(); // 检查token是否为空,或者距离过期不足60秒(预留缓冲时间避免边缘情况) if (currentToken == null || System.currentTimeMillis() >= currentToken.getExpirationTime() - 60 * 1000) { try { googleCredentials.refresh(); } catch (IOException e) { e.printStackTrace(); // 建议替换为更健壮的错误处理,比如抛出自定义异常让上层处理 throw new RuntimeException("Failed to refresh FCM access token", e); } } return googleCredentials.getAccessToken().getTokenValue(); }
3. 额外的代码优化建议
(1)修复static块的异常处理和资源泄漏
你的static块没有处理文件读取的异常,也没有关闭输入流,可能导致类初始化失败或资源泄漏。改用try-with-resources优化:
static { try (FileInputStream refreshTokenStream = new FileInputStream("C:/prj/proserviceaccoutkey.json")) { googleCredentials = GoogleCredentials.fromStream(refreshTokenStream) .createScoped("https://www.googleapis.com/auth/firebase.messaging"); FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(googleCredentials).build(); // 如果后续需要使用Firebase Admin SDK的其他功能,建议初始化FirebaseApp // FirebaseApp.initializeApp(options); } catch (IOException e) { throw new RuntimeException("Failed to load service account key file", e); } }
(2)考虑使用Firebase Admin SDK替代手动REST调用
手动调用FCM REST API需要自己管理AccessToken,容易出错。推荐直接使用Firebase Admin SDK,它会自动处理token的刷新、过期管理,代码更简洁可靠:
// 初始化(static块中完成) static { try (FileInputStream serviceAccount = new FileInputStream("C:/prj/proserviceaccoutkey.json")) { FirebaseOptions options = new FirebaseOptions.Builder() .setCredentials(GoogleCredentials.fromStream(serviceAccount)) .build(); FirebaseApp.initializeApp(options); } catch (IOException e) { throw new RuntimeException("Failed to initialize Firebase Admin SDK", e); } } // 发送数据消息的方法 public static void sendDataMessage() throws FirebaseMessagingException { Message message = Message.builder() .putData("key1", "value1") .putData("key2", "value2") .setToken("DEVICE_REGISTRATION_TOKEN") .build(); String response = FirebaseMessaging.getInstance().send(message); System.out.println("Successfully sent message: " + response); }
4. 其他可能的排查点
- 检查服务账号密钥文件是否有变动,比如权限被修改或密钥过期;
- 确认你的服务器时间是否准确,时间偏差过大可能导致token过期判断出错;
- 查看FCM控制台的错误日志,是否有更详细的401错误说明(比如token无效、权限不足等)。
内容的提问来源于stack exchange,提问作者Abhishek
相关产品推荐
相关产品推荐

