用户看到的是空白、卡住或一直加载,系统里却可能已经有一个明确的信号:接口返回了 401。
最近一次修复针对的就是这种错位。登录令牌过期后,前端仍只检查本地存储里有没有令牌字符串,不检查令牌是否还有效。页面因此保持“已登录”的外观,但余额停在同步中,会话列表没有内容,用户也收不到提示。
这不是一个单独页面的 Bug,而是登录状态判断与接口真实状态脱节。
现象:页面还在,业务已经失效
问题出现时,用户仍然可以看到应用外壳。真正需要数据的请求陆续返回 401,页面组件各自处理失败结果,有的停在 loading,有的显示空列表,有的没有任何反馈。
这种状态比直接跳转登录页更难排查,因为页面没有崩溃,浏览器也没有明显报错。用户的描述通常是“突然不能用了”。
根因有两个:
- 本地
yc_token只作为“字符串是否存在”的标记; - 应用内的请求分散在几十个文件,没有统一的 401 出口。
修复:在请求边界识别失效
这次修改没有要求逐个页面补判断,而是在应用入口包一层 window.fetch,把判定条件收紧到三点:
- 请求是同源
/api/路径; - 请求不在登录、注册和票据登录等合法返回 401 的白名单里;
- 本地确实存在登录令牌。
满足条件后,才把 401 解释为登录失效,并触发统一提示。外部签名 URL 不会被误判,流式响应也不读取 body,只观察 HTTP status,避免影响聊天和视频编导流程。
这个边界很重要。全局拦截器如果只看状态码,容易把登录接口本身的“凭据错误”当成“会话过期”;如果不区分同源请求,也可能误伤对象存储或其他外部服务。
交互:先保住用户正在做的事
修复没有直接清空登录态,而是分两层提示。
第一层是弹窗,告诉用户会话已失效,并提供重新登录入口。用户可以选择稍后处理,先复制还没有保存的内容。
第二层是顶部常驻提示条。用户关掉弹窗后,页面仍保留可见的恢复入口,避免回到“没有提示的死状态”。
多标签页也是一个边界:如果另一个标签页已经完成续期,当前页面重新检查后应接管新的有效令牌,而不是把它删掉。否则用户在一个标签页登录,另一个标签页却把新令牌清除,问题会从单页失效变成跨标签页互相干扰。
测试:验证状态转换,而不是只测组件渲染
这次变更补了三类测试:
- 失效令牌触发弹窗和常驻提示;
- 登录、注册等白名单接口的 401 不触发全局失效;
- 多标签页已有新令牌时,当前页面能复用有效状态。
测试重点不是“按钮是否出现”,而是确认请求状态、登录状态和用户反馈之间的转换。只有把这三者放在一起验证,才能避免页面看似正常、用户却无法继续工作的情况。
结论:鉴权错误也是产品状态
401 不是只留给开发者看的网络状态。对用户来说,它意味着“当前工作无法继续”,必须有明确的恢复路径。
前端鉴权设计至少要回答三个问题:
- 哪些 401 表示凭据错误,哪些表示会话过期;
- 失效时用户正在编辑的内容如何保留;
- 多标签页和重试发生时,哪个令牌拥有更高的有效性。
把这三个问题写进代码和测试,登录失效就不再是一张空白页,而是一个可理解、可恢复的产品状态。
■