基础配置如下:
const base = import.meta.env.VITE_APP_BASE_SSO;
const back_uri = import.meta.env.VITE_APP_BASE_HTTP;
const config = {
authority: base,
client_id: 'xxxxxxxxx',
redirect_uri: back_uri,
response_type: 'id_token token',
scope: 'xxxxxxxxxxxx',
silent_redirect_uri: back_uri,
automaticSilentRenew: true,
silentRequestTimeout: 30000,
userStore: new WebStorageStateStore({ store: window.localStorage })
};
const userManager = new UserManager(config);登录是成功的,token过期前60秒会自动静默刷新
userManager.events.addUserLoaded(async (user: User) => {
account.access_token = user.access_token;
account.expiresIn = user.expires_at;
account.token_type = user.token_type;
let res = await getOwnerUserAPI();
account.userid = res.userid;
account.username = res.userName;
if (isFirstLogin) {
isFirstLogin = false;
router.push('/home');
}关键是静默刷新的时候addUserLoaded事件执行了,token也更新了,但过了几秒后又执行了addSilentRenewError,提示静默刷新失败 Error: Frame window timed out
// 静默刷新token失败处理
userManager.events.addSilentRenewError(error => {
console.error('静默刷新失败', error);
// 可以在这里处理刷新失败后的逻辑,比如跳转到登录页
});试过延长silentRequestTimeout依旧无效,silentRequestTimeout的时间是30000毫秒基本上也能处理静默刷新的响应了吧
🔍 问题分析
✅ 正常情况
automaticSilentRenew: true表示 token 过期前会自动触发静默刷新。<iframe>请求silent_redirect_uri,并从中解析 token。addUserLoaded。addSilentRenewError。🚨 异常现象
你观察到:
addUserLoaded被触发(说明确实收到新的 token)。addSilentRenewError,提示:Frame window timed out。这说明一个问题:token 刷新操作其实成功了,但 oidc-client 的某个内部逻辑仍然以为超时了。
✅ 常见原因及解决方案
1.
silent_redirect_uri页面没有正确调用userManager.signinSilentCallback()这是最常见的原因:
🔧 检查:
你配置了:
但这个地址必须指向一个页面,该页面在被 iframe 加载时能够调用:
👉 解决方案:
silent-renew.html或/silent-renew页面。⚠️ 注意:
silent_redirect_uri指向的是 SPA 的首页(比如你配置的back_uri是根目录),当 iframe 加载这个地址时,可能没有自动执行signinSilentCallback(),就导致了超时。2. token 实际刷新了两次(可能是状态管理冲突)
你提到
addUserLoaded成功,然后几秒后报错。这可能是因为:UserManager认为状态不一致(比如 store 中还没更新),再尝试刷新一次,导致 iframe 超时。🔧 解决方案:
在你的
addUserLoaded回调中加入日志,检查是否重复调用了刷新流程。3. 浏览器环境限制 iframe 的行为(尤其是 Chrome 的隐私政策)
部分浏览器(尤其是 Chrome 和 Safari)对 第三方 iframe 的 Cookie、
Storage、跳转等行为进行了严格限制,尤其在跨域下。🔧 解决方案:
确保:
silent_redirect_uri和主站处于同一域名下。可以设置 SSO 服务端响应头如下:
并设置:
✅ 推荐的最终配置示例