浏览器关闭实现登出(后端清token)

实现浏览器关闭后,后端记录用户登出日志的最佳方式是通过前端发送请求来通知后端进行记录。以下是一种常见的实现方式,重点就是如何区分用户行为是页面刷新还是关闭浏览器。

js 复制代码
// 写在APP.vue
mounted() {  
    window.addEventListener("beforeunload", () => this.beforeunloadHandler())
    window.addEventListener("unload", () => this.unloadHandler())  
},
  
destroyed() {
    window.removeEventListener("beforeunload", () => this.beforeunloadHandler())
    window.removeEventListener("unload", () => this.unloadHandler())
    clearInterval(this.timer)
},

methods:{
     beforeunloadHandler() {
      this.beforeUnloadTime = new Date().getTime()
    },
    unloadHandler() {
      this.gapTime = new Date().getTime() - this.beforeUnloadTime
      if (this.gapTime <= 5) { //判断是窗口关闭还是刷新,小于5代表关闭,否则就是刷新。
        // 这里是关闭浏览器
        logout()
      }
    },
}

但是经测试,发现上面这种浏览器关闭事件并不是一种可靠的方式来捕捉用户的登出操作,后端并非百分百接收到logout请求,经查资料得知,在unload阶段发送的异步请求是不可靠的,有可能被cancel。后面又尝试了fetch,设置了keepalive(即使浏览器关闭,请求照样发送), 但是又发现gapTime<=5的判断条件也存在兼容性问题,不同浏览器的时间差存在差异。此外还存在一些特殊情况:用户可能直接关闭浏览器窗口、断开网络连接或发生其他异常情况,导致浏览器关闭事件无法被触发,因此pass掉上述方案。

后面也尝试了心跳机制(websocket),也存在局限性,pass。

最后想到了一种最简单,最朴实的方式: 开启定时器每秒往localStorage写入当前时间lastRecordTime(new Date().getTime()), 在请求拦截器中给每个接口请求头带上两个时间,最后一次写入时间lastRecordTime和当前时间nowTime, 后端只要把两个时间相减, 超过5s(自定义)就算登出,清掉redis里相应的token。

js 复制代码
// 写在APP.vue
created (){
    // 每秒写入一次时间
    this.timer = setInterval(() => {
    // 这个判断代表登录成功后才开始写入时间
      if(localStorage.getItem('token')) {
        localStorage.setItem('lastRecordTime', new Date().getTime())
      }
    }, 1000)
  }

另外需要注意, 在登录成功的地方要立即写入一次时间, 不然有BUG。

js 复制代码
  // 写在请求拦截器
  const headers = config.headers;
  /** 用于判断用户是否关闭过浏览器,如果关闭则跳转至登录页面,以及及时清理redis中的token */
  if (localStorage.getItem('lastRecordTime')) {
    headers.lastRecordTime = localStorage.getItem('lastRecordTime');
  }
  headers.nowTime = new Date().getTime();

总结一下,目前没发现哪种方式可以提供一种可靠的通信方式去通知后端清除token, 通过两个时间差的方式相对靠谱。

相关推荐
Data analyse4561 小时前
rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路
前端·网络·算法·数据分析
xiaogai_gai2 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
环境栈笔记2 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
赵大仁3 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu3 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!3 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
史瓦辛格3 小时前
给 DeepSeek Harness 写费用插件:把「这次会话花了多少钱」算准
前端·deepseek
星光不问赶路人3 小时前
Vue 3 + TypeScript 中 enum 的使用实践
前端·vue.js
鸥梨菌Honevid3 小时前
UVM 中 REQ 和 RSP 类型详解:从通信模型到 item_done 与 put_response
java·前端·servlet
IT_陈寒3 小时前
Python多进程共享变量这个坑,差点让我加班到凌晨
前端·人工智能·后端