前端登录白屏排查-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(RSAPrivateKeynull){thrownewEcpSysException(invalid_key,nonexistent!!);}逻辑很清晰前端请求/core/service时携带了一个 RSA 公钥标识后端去缓存中查对应的 RSA 私钥查不到就直接抛异常。问题来了为什么登录成功了RSA 密钥却不在缓存里2.2 分析请求链路梳理正常流程的请求时序Redis/缓存后端前端Redis/缓存后端前端页面重载后路由守卫触发1. 扫码登录写入 token登录成功返回 token2. location.reload() 刷新页面3. GetRsaPublicKey获取公钥写入 RSA 密钥对公钥私钥返回 RSA 公钥4. /core/service业务请求带公钥用公钥查私钥找到私钥 ✓正常响应正常情况下步骤 3 的密钥交换在步骤 4 的业务请求之前完成所以私钥已经在缓存里了。但实际出问题的时序是Redis/缓存后端前端Redis/缓存后端前端步骤4GetRsaPublicKey还没返回...1. 扫码登录写入 token登录成功返回 token2. location.reload() 刷新页面3. /core/service业务请求先到用公钥查私钥null密钥交换还没完成500 异常 → 白屏​核心矛盾​location.reload()之后业务请求/core/service抢在了 RSA 密钥交换接口的前面。2.3 前端代码验证带着上述假设去查前端源码很快确认了​两处缺少等待机制​​路由守卫router/nportal.js// 问题代码dispatch 后没有 awaitfire-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 存储 → 密钥交换 → 业务请求这条链路上每个环节都应串行等待避免竞态这类问题在生产环境中往往表现为偶发性白屏或部分用户反馈异常排查难度不高但容易被忽视。建议在登录流程等关键链路上建立完整的异步等待机制从源头杜绝竞态风险。