Angular 5中iframe静默刷新Identity Server令牌时ngIdle空闲状态被打断问题
解决ngIdle空闲计时被Identity Server刷新iframe打断的问题
问题分析
你的场景里,用iframe请求Identity Server的令牌刷新端点时,意外重置了ngIdle的空闲计时器,但访问其他URL的iframe却没有这个问题。这大概率是因为Identity Server的页面在加载过程中,触发了ngIdle所监听的用户交互类事件(比如窗口焦点变化、某些冒泡到顶层窗口的事件),导致ngIdle误判用户有操作,进而重置了计时。
可行的解决方案
1. 优化iframe的创建方式,避免触发不必要的事件
首先,给iframe添加隐藏属性和禁止焦点的设置,减少它对顶层窗口的干扰,同时请求完成后及时移除iframe:
refreshToken(){ let iframe = document.createElement('iframe'); iframe.id = "refreshToken"; // 添加隐藏样式,禁止获取焦点 iframe.style.display = 'none'; iframe.style.visibility = 'hidden'; iframe.tabIndex = -1; // 加载完成后延迟移除iframe,确保请求处理完成 iframe.onload = () => { setTimeout(() => { document.body.removeChild(iframe); }, 100); }; // 修正src参数格式,直接用&而非转义后的& iframe.setAttribute('src','/connect/token?client_id=client1&client_secret=secret&grant_type=authorization_code&code=hdh922&redirect_uri=https://myapp.com/callback'); document.body.appendChild(iframe); }
2. 临时暂停/恢复ngIdle计时
在刷新令牌前后,手动控制ngIdle的状态,从根源上避免刷新过程干扰计时:
refreshToken(){ // 刷新前暂停ngIdle,停止监听用户交互 this.ngIdle.stop(); this.ngIdle.reset(); let iframe = document.createElement('iframe'); iframe.id = "refreshToken"; iframe.style.display = 'none'; iframe.onload = () => { setTimeout(() => { document.body.removeChild(iframe); // 刷新完成后恢复ngIdle的监听 this.ngIdle.watch(); }, 100); }; iframe.setAttribute('src','/connect/token?client_id=client1&client_secret=secret&grant_type=authorization_code&code=hdh922&redirect_uri=https://myapp.com/callback'); document.body.appendChild(iframe); }
3. 替换iframe为fetch请求(优先推荐,需CORS支持)
如果Identity Server的令牌端点允许CORS请求,建议用fetch直接发送刷新请求,完全避免iframe带来的事件干扰:
async refreshToken() { // 刷新前暂停ngIdle this.ngIdle.stop(); try { const response = await fetch('/connect/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ client_id: 'client1', client_secret: 'secret', grant_type: 'authorization_code', code: 'hdh922', redirect_uri: 'https://myapp.com/callback' }) }); const tokenData = await response.json(); // 处理新令牌(比如存入本地存储、更新请求拦截器等) } catch (error) { // 处理刷新失败的情况 console.error('令牌刷新失败:', error); } finally { // 无论成功失败,都恢复ngIdle的监听 this.ngIdle.watch(); } }
4. 调整ngIdle的事件监听规则
如果以上方案都不适用,可以修改ngIdle的配置,过滤掉来自iframe的事件触发:
import { Idle } from '@ng-idle/core'; constructor(private idle: Idle) { // 自定义ngIdle监听的事件,移除容易被iframe触发的事件(比如focus) idle.setInterrupts(['mousemove', 'keydown', 'scroll', 'touchstart']); // 添加事件过滤器,忽略来自iframe的事件 idle.interruptFilter = (event) => { // 判断事件源是否为iframe,若是则不触发计时重置 if (event.target instanceof HTMLIFrameElement) { return false; } // 其他情况允许触发重置 return true; }; }
总结
优先尝试方案3(用fetch替换iframe),如果受限于CORS配置,再用方案2(手动暂停恢复ngIdle)或方案1(优化iframe),最后考虑调整ngIdle的事件过滤规则。这些方法都能有效解决Identity Server的iframe刷新打断空闲计时的问题。
内容的提问来源于stack exchange,提问作者chaitra
相关产品推荐
相关产品推荐

