You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用JavaScript判断用户离开当前页面时的跳转目标地址

嘿,这个场景我太熟了!要实现ASP.NET持久登录下跨域名跳转自动登出,你最初的onbeforeunload思路方向对,但真的不用费劲遍历所有锚点——其实有更高效靠谱的方案,咱们一步步拆解:

核心思路:精准区分同域/跨域跳转

用户离开页面的原因五花八门(刷新、关闭标签、同域跳转、跨域跳转),我们只需要在跨域跳转这个特定场景下触发登出操作就行。

1. 提前拦截点击事件,判断跳转目标

不用等页面要卸载了才处理,直接给页面上所有可跳转元素绑定点击事件,提前判断目标地址的域名:

  • 先写个提取域名的工具函数:
function getDomain(url) {
  const a = document.createElement('a');
  a.href = url;
  // 兼容大部分浏览器,IE下可额外处理hostname
  return a.hostname.toLowerCase();
}
  • 用事件委托监听所有点击行为(连动态生成的a标签也能覆盖):
document.addEventListener('click', function(e) {
  let target = e.target;
  // 找到最外层的a标签(比如a里嵌套了span、icon的情况)
  while (target && target.tagName !== 'A') {
    target = target.parentNode;
  }
  if (target && target.href) {
    const targetDomain = getDomain(target.href);
    const currentDomain = getDomain(window.location.href);
    // 检测到跨域跳转时触发登出
    if (targetDomain !== currentDomain) {
      fetch('/Account/Logout', { 
        method: 'POST', 
        credentials: 'include' // 必须带上会话Cookie,不然服务端认不出当前用户
      })
      .catch(err => console.error('登出请求失败:', err));
      // 不用阻止跳转,我们只是提前销毁会话
    }
  }
});

2. 用Beacon API处理兜底场景

有些跳转不是点击a标签触发的(比如脚本调用window.location.href、页面刷新/关闭),这时候用onbeforeunload配合Beacon API最稳妥——它是浏览器专门为页面卸载时的异步请求设计的,不会阻塞页面卸载,还能保证请求尽量被发送:

window.addEventListener('beforeunload', function(e) {
  // 这里默认兜底处理:无法确定跳转目标时,发送登出请求
  const formData = new FormData();
  formData.append('logout', 'true');
  navigator.sendBeacon('/Account/Logout', formData);
});

为什么不用普通AJAX?因为浏览器现在会限制onbeforeunload里的同步请求,异步请求又容易被中断,Beacon是后台异步处理,可靠性高很多。

3. ASP.NET服务端配合实现登出

服务端的Logout接口要兼容两种请求(fetch的POST和Beacon的POST),比如MVC的Action示例:

[HttpPost]
public async Task<ActionResult> Logout()
{
    // 销毁当前会话
    Session.Abandon();
    // 如果用了ASP.NET Identity的持久登录,记得调用SignOutAsync
    await _signInManager.SignOutAsync();
    
    // 清除会话Cookie
    if (Request.Cookies["ASP.NET_SessionId"] != null)
    {
        var cookie = new HttpCookie("ASP.NET_SessionId")
        {
            Expires = DateTime.Now.AddDays(-1),
            HttpOnly = true
        };
        Response.Cookies.Add(cookie);
    }
    // Beacon请求不需要返回复杂内容,空响应即可
    return new EmptyResult();
}

4. 避坑小贴士

  • 别遍历所有锚点:事件委托比逐个绑定高效太多,还能覆盖动态生成的元素。
  • 跨域请求不用慌:登出请求是同域的,和跳转的跨域地址无关,不会有跨域问题。
  • Beacon的限制:它不能自定义请求头,所以依赖会话Cookie做身份验证是最稳妥的方式。

内容的提问来源于stack exchange,提问作者Oloff Biermann

火山引擎 最新活动