news 2026/7/5 14:35:23

前端登录白屏排查-RSA时序竞态

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端登录白屏排查-RSA时序竞态

记一次前端登录白屏问题排查:RSA 密钥时序竞态导致的服务端校验失败

1. 问题现象

生产环境中,部分用户反馈打开门户系统登录页后,​扫码登录成功或手动刷新页面时会出现白屏​,页面无任何报错信息,F12 控制台也无明显错误。而同一浏览器版本的其他用户使用正常,问题具有偶发性,难以稳定复现。

2. 排查思路

面对这种"部分用户出问题、大部分正常"的场景,排查思路如下:

确认现象 → 后端日志定位异常时间点 → 分析请求链路 → 前端代码验证 → 定位根因

2.1 后端日志定位

首先在网关/后端服务日志中,以出现白屏的时间窗口为线索过滤请求记录。关键发现如下:

时间戳线程事件
08:10:55.089exec-293登录接口返回成功,token 已写入缓存
08:10:55.097exec-293写入登录审计记录
08:10:55.155exec-307/core/service请求异常

可以看到登录本身是成功的(线程 exec-293),但紧接着仅 ​66ms 后​,另一个请求(线程 exec-307)就触发了异常。

异常堆栈定位到RefreshTokenFilter.java

// RefreshTokenFilter.java:119StringRSAPrivateKey=(String)uCache.get(RSAPublicKery,true);if(RSAPrivateKey==null){thrownewEcpSysException("invalid_key,nonexistent!!");}

逻辑很清晰:前端请求/core/service时携带了一个 RSA 公钥标识,后端去缓存中查对应的 RSA 私钥,查不到就直接抛异常。

问题来了:为什么登录成功了,RSA 密钥却不在缓存里?

2.2 分析请求链路

梳理正常流程的请求时序:

Redis/缓存后端前端Redis/缓存后端前端页面重载后,路由守卫触发1. 扫码登录写入 token登录成功(返回 token)2. location.reload() 刷新页面3. GetRsaPublicKey(获取公钥)写入 RSA 密钥对(公钥+私钥)返回 RSA 公钥4. /core/service(业务请求,带公钥)用公钥查私钥找到私钥 ✓正常响应

正常情况下,步骤 3 的密钥交换在步骤 4 的业务请求之前完成,所以私钥已经在缓存里了。

但实际出问题的时序是:

Redis/缓存后端前端Redis/缓存后端前端步骤4(GetRsaPublicKey)还没返回...1. 扫码登录写入 token登录成功(返回 token)2. location.reload() 刷新页面3. /core/service(业务请求先到!)用公钥查私钥null(密钥交换还没完成!)500 异常 → 白屏

核心矛盾​:location.reload()之后,业务请求/core/service抢在了 RSA 密钥交换接口的前面。

2.3 前端代码验证

带着上述假设去查前端源码,很快确认了​两处缺少等待机制​:

​路由守卫(router/nportal.js

// 问题代码:dispatch 后没有 await,fire-and-forgetstore.dispatch('GetRsaPublicKey').then()// 紧接着就放行路由,页面开始渲染,业务组件立即发起请求next()

路由守卫触发了GetRsaPublicKey的 dispatch,但没有等它完成就放行了。如果这个接口响应慢,业务组件的加密请求就会先到达后端。

登录成功回调(Login.vue

// 问题代码:存完 token 后直接 reload,没有任何等待LoginRear:function(d){// ... 存 token ...location.reload()// 立即刷新,不等密钥就绪}

两处代码的共同点:都是 fire-and-forget 模式,发起异步操作后不等待结果就继续往下走。

2.4 为什么只有部分用户出问题?

这是一个典型的时序竞态(Race Condition)问题,能不能触发取决于网络延迟:

  • 网络延迟低​:密钥交换接口 50ms 就返回了,location.reload()后页面加载再发业务请求至少需要 100ms+,此时密钥已在缓存中,不会出问题
  • 网络延迟高​:密钥交换接口需要 200ms+,而页面 reload 后业务请求可能 100ms 就发出去了,此时密钥还没写入缓存,触发异常

大多数用户网络条件好,时序上碰巧避开了这个坑;网络稍慢的用户就会稳定复现。

3. 修复方案

修复原则:只在 key 不存在时才增加等待,已有 key 时零延迟,不影响正常流程。

3.1 路由守卫修复

// 修复后:确保 RSA 公钥就绪后再放行路由awaitstore.dispatch('GetRsaPublicKey')next()

3.2 登录回调修复

// 修复后:async 函数,reload 前兜底检查 keyLoginRear:asyncfunction(d){// ... 存 token ...// 兜底:如果 RSA 密钥尚未就绪,等待它完成if(!store.state.rsaPublicKey){awaitstore.dispatch('GetRsaPublicKey')}location.reload()}

修复前后对比

场景修复前修复后
正常用户(低延迟)碰巧不出问题行为不变,零额外等待
慢网络用户稳定白屏等待密钥就绪后再 reload,不再白屏
极端情况(密钥接口超时)白屏会被接口自身的超时/错误机制捕获

4. 经验总结

维度要点
排查策略先从后端日志锁定异常时间点和线程,再逆推前端请求链路,比盲目在前端打断点高效得多
问题本质时序竞态(Race Condition)—— 多个异步操作的完成顺序不确定,在特定条件下会暴露
代码规范关键异步操作必须 await,不能 fire-and-forget,尤其是在涉及安全凭证(token、密钥)的场景
影响面判断“只有某个人出问题” ≠ “个性化问题”,很可能是环境差异触发了共性 bug,需要从代码层面根治
前端安全链路登录 → token 存储 → 密钥交换 → 业务请求,这条链路上每个环节都应串行等待,避免竞态

这类问题在生产环境中往往表现为"偶发性白屏"或"部分用户反馈异常",排查难度不高但容易被忽视。建议在登录流程等关键链路上建立完整的异步等待机制,从源头杜绝竞态风险。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 14:33:25

【ESP32】ESP-IDF开发环境搭建(cursor)

1 前言 ESP-IDF 是乐鑫主推的ESP32的开发框架,旨在协助用户快速开发物联网 (IoT) 应用,可满足用户对 Wi-Fi、蓝牙、低功耗等方面的要求。 [https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/get-started/index.html] 如需在 ESP32 上使用…

作者头像 李华
网站建设 2026/7/5 14:31:41

设计模式——抽象工厂

一、概述 抽象工厂模式,作为创建型设计模式中的一员,有着独特的魅力与价值。与我们常见的普通工厂模式相比,它无疑更上一层楼,引入了抽象工厂这一关键概念,同时还涵盖了抽象产品和抽象零件的理念。 普通工厂模式往往聚焦于具体产品的创建,而抽象工厂模式则站在了更高的维…

作者头像 李华
网站建设 2026/7/5 14:28:58

ConvNeXt 的 torchvision 版本 推理实现

ConvNeXt 的 torchvision 版本 推理实现 flyfish import torch import os from PIL import Image from torchvision import transforms # 引入你自定义的ConvNeXt模型文件(确保和本文件同目录) from convnext_tiny import convnext_tiny# 配置 # 本地…

作者头像 李华
网站建设 2026/7/5 14:24:47

SQL优化

目录 1. 插入数据优化 2.主键优化 3.order by 排序优化 4.group by 分组优化 5.limit优化 count 的几种用法 count(主键) count(字段) count (1) count (*) 7.update语句优化 1. 插入数据优化 1.1 批量插入数据 如果…

作者头像 李华