浏览器关闭时发送请求使token过期的方法,不受刷新和关闭标签页影响?

新手上路,请多包涵

监听浏览器关闭时需要【发送请求给后端】清除token,不受刷新和关闭标签页影响,有人搞过吗?

给了其它方案给设计师,但他们安全那边就是要关闭浏览器时就得发送请求给后端使token过期

阅读 2.3k
avatarAI BotBETA

要在浏览器关闭时发送请求使token过期,并且不受刷新和关闭标签页的影响,你可以使用beforeunload事件。这个事件会在窗口、文档及其资源将要卸载时触发。你可以在这个事件的处理函数中发送一个请求到后端来使token过期。

下面是一个简单的示例代码,演示了如何使用beforeunload事件来发送请求使token过期:

window.addEventListener('beforeunload', function (e) {
  // 在这里发送请求到后端使token过期
  // 你可以使用fetch、axios或其他你喜欢的HTTP库来发送请求
  fetch('/api/token/expire', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ token: 'your_token_here' }), // 将要过期的token作为请求体发送
  })
    .then(response => {
      // 处理响应,如果需要的话
    })
    .catch(error => {
      // 处理错误,如果需要的话
    });

  // Chrome要求返回一个非空字符串,否则不会触发事件
  e.preventDefault();
  e.returnValue = '';
});

请注意,由于浏览器安全策略的限制,beforeunload事件的处理函数在大多数浏览器中是异步执行的,这意味着你不能在这个函数中等待异步操作(如HTTP请求)完成。因此,你可能需要采取其他策略来确保token在浏览器关闭之前被成功过期,例如使用WebSockets或其他长连接技术。

此外,还需要注意的是,即使你成功地在浏览器关闭时发送了使token过期的请求,仍然有可能存在一些边缘情况,例如用户直接关闭计算机或杀死浏览器进程,导致请求无法成功发送。因此,后端应该有一个机制来处理这种情况,例如设置一个token的过期时间,并在验证token时检查它是否已经过期。

7 个回答

做过这个功能,但只适合主流浏览器,代码如下:

 let beforeUnloadTime = null;
 window.addEventListener("beforeunload", e => beforeunloadHandler(e));
 window.addEventListener("unload", e => unloadHandler(e))

 function beforeunloadHandler () {
    beforeUnloadTime = new Date().getTime();
 }

 function unloadHandler(e) {
  let unloadTime = new Date().getTime() - beforeUnloadTime;
  // 判断是窗口关闭还是刷新
  if (unloadTime <= 5) {
    await fetch(``, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Edc-Authorization":
          "Bearer " + localStorage.getItem("Token")
      },
      // 保持连接
      keepalive: true
    });
  }
 }

如果想更好的解决这个问题,建议使用socket做心跳连接,一直ping,如何断开连接,表示浏览器关闭了,这时候后端处理这个逻辑。

问题:有没有可能同一个浏览器打开了多个页面,我只关闭了其中一个页面,但是我切换了另外一个页面时,并不想退出登录,那么我们应该在这个浏览器关闭这个域名的最后一个网页时处理这个逻辑。

所以我们应该记住当前浏览器打开几个页面:

window.onload = function() {
    const count = localStorage.getItem("BrowserWindowNumber");
    if (count === null) {
      localStorage.setItem("BrowserWindowNumber", 1);
    } else if (~~count === -1) {
      localStorage.setItem("BrowserWindowNumber", 1);
    } else {
      localStorage.setItem("BrowserWindowNumber", parseInt(count) + 1);
    }
  };

  // 当窗口关闭时,减少计数器
  window.onunload = function() {
    const count = localStorage.getItem("BrowserWindowNumber");
    if (count !== null) {
      localStorage.setItem("BrowserWindowNumber", parseInt(count) - 1);
    }
  };



function beforeunloadHandler () {
    beforeUnloadTime = new Date().getTime();
 }

 function unloadHandler(e) {
  let unloadTime = new Date().getTime() - beforeUnloadTime;
  // 判断是窗口关闭还是刷新
  if (unloadTime <= 5) {
    const count = localStorage.getItem("BrowserWindowNumber");
    if(~~count === 0) {
        await fetch(``, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Edc-Authorization":
            "Bearer " + localStorage.getItem("Token")
        },
        // 保持连接
        keepalive: true
      });
    }
  }
 }

Beacon API 用于发送异步和非阻塞的信标请求。信标请求不需要响应,且浏览器保证在页面卸载前初始化并运行完成。

或者每隔一段时间(如一分钟)发送一次请求,当后端超过一端时间没有收到请求时就认为 token 过期。

做不到,至少公域浏览器做不到。如果你们是内部需求,可以搞一些奇奇怪怪的方案,否则的话就很难。

哦,我想到一个方案,用 service worker 的 notification API,跟服务器做心跳,断了就过期 token。

这个做过类似的,但是我只做了Chrome浏览器,我当时做的时候关闭浏览器的时候清除本地的localStorage,然后使用onbeforeunload去做的话,无法实现,看到一篇文章中说浏览器刷新和关闭的时候load事件会有时间差的存在,于是我采用了这样的办法。但是记住我只测试了Chrome浏览器,而且这种方式不知道适不适合你们那么严格的要求。代码如下:

// 判断是刷新还是关闭浏览器,刷新不删除localStorage,否则删除
onMounted(() => {
  let beforeTime = 0,
    leaveTime = 0;
  window.onunload = () => {
    leaveTime = new Date().getTime() - beforeTime;
    if (leaveTime <= 5) {
      console.log("====关闭=====");
      localStorage.clear();
    } else {
      console.log("====刷新=====");
    }
  };

  window.onbeforeunload = () => {
    beforeTime = new Date().getTime();
  };
});

使用Beacon结合visibilitychange事件
很多统计用户页面停留时间就是这么干的
注意不要用 unload 或 beforeunload 事件

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", data);
  }
});

这个需求其实就是想让客户端浏览器主动通知服务器 token 需要过期,用浏览器关闭的方式还是有很多方式可以绕开的:断网、关机都不会触发浏览器的关闭事件。如果非要按照这个需求去处理,你还不如结合 websocket 用长连接方式,断开连接后服务端就把对应连接关联的 token 给过期掉,这种可能更靠谱点(当然也会有其他问题,看取舍了)。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏