From 225af89e411791e645122b9cb7e03b3b5b450b57 Mon Sep 17 00:00:00 2001 From: iven Date: Sun, 7 Jun 2026 12:59:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(app):=20Token=20=E5=88=B7=E6=96=B0=E5=BD=BB?= =?UTF-8?q?=E5=BA=95=E5=A4=B1=E8=B4=A5=E6=97=B6=E9=80=9A=E7=9F=A5=20AuthBl?= =?UTF-8?q?oc=20=E6=B4=BE=E5=8F=91=20AuthExpired?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 审计 9a-AUTH-01 修复: - ApiClient 新增 onAuthFailed 回调,在 401 刷新失败后触发 - app.dart 注册回调:派发 AuthExpired → GoRouter 重定向到登录页 - 之前刷新失败只清除内存 token,用户停留在死页面 - 现在 401 → 尝试刷新 → 失败 → AuthExpired → 自动跳转登录 --- app/lib/app.dart | 5 +++++ app/lib/data/remote/api_client.dart | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/lib/app.dart b/app/lib/app.dart index 5c47d6c..1d89a02 100644 --- a/app/lib/app.dart +++ b/app/lib/app.dart @@ -72,6 +72,11 @@ class NuanjiApp extends StatelessWidget { } }); + // Token 刷新彻底失败时 → 派发 AuthExpired → 路由重定向到登录页 + apiClient.onAuthFailed = () { + authBloc.add(const AuthExpired()); + }; + return MultiRepositoryProvider( providers: [ RepositoryProvider.value(value: apiClient), diff --git a/app/lib/data/remote/api_client.dart b/app/lib/data/remote/api_client.dart index 45f64bc..a7d5f9d 100644 --- a/app/lib/data/remote/api_client.dart +++ b/app/lib/data/remote/api_client.dart @@ -35,6 +35,12 @@ class ApiClient { /// 使用回调模式避免 ApiClient ↔ AuthRepository 循环依赖。 Future Function()? onRefreshToken; + /// 认证彻底失败回调 — 刷新 token 失败后由 app.dart 注册 + /// + /// 通知 AuthBloc 派发 AuthExpired 事件,触发路由重定向到登录页。 + /// 解决审计 9a-AUTH-01:刷新失败时用户不会被留在死页面。 + void Function()? onAuthFailed; + /// 是否正在刷新 token(防止并发 401 触发多次刷新) bool _isRefreshing = false; @@ -95,8 +101,9 @@ class ApiClient { _isRefreshing = false; } - // 刷新失败或无刷新回调 → 清除 token + // 刷新失败或无刷新回调 → 清除 token,通知全局认证失效 _token = null; + onAuthFailed?.call(); } handler.next(error); },