用戶直接關閉瀏覽器(非點擊「退出」鏈接)導致服務端無法感知會話結束,Session 滯留佔用資源。本文對比五種主流解決方案的原理、優缺點與適用場景。
| 方案 | 核心機制 | 檢測延遲 | 資源開銷 | 可靠性 | 適用場景 |
|---|---|---|---|---|---|
| 1. 隱藏 IFrame 心跳 | 定期異步請求保活 | 秒級(如 10s) | 高(頻繁請求) | 高 | 實時性要求高的內部系統 |
| 2. 無過期 Cookie | 瀏覽器關閉自動銷毀 Session Cookie | 即時 | 極低 | 中(用戶可禁用 Cookie) | 簡單應用、非關鍵業務 |
| 3. onunload 事件跳轉 | 頁面卸載時請求登出接口 | 即時 | 低 | 低(多標籤頁誤觸發) | 單頁應用、嚴禁多開 |
| 4. 活動時間戳 + 定時掃描 | 記錄最後操作時間,後台掃描清理 | 分鐘級(掃描間隔) | 中(DB 寫入+定時任務) | 高 | 標準 Web 應用、推薦方案 |
| 5. 被動等待 Session 超時 | 依賴容器 Session Timeout 機制 | 配置值(如 20min) | 零開發 | 低(滯留佔用資源) | 低併發、非核心系統 |
// 頁面嵌入
<iframe id="heartbeat" src="/keepalive.ashx" style="display:none"></iframe>
// keepalive.ashx (服務端)
public void ProcessRequest(HttpContext context) {
// 僅更新 Session 最後訪問時間
context.Session["LastActive"] = DateTime.Now;
context.Response.Write("ok");
}
// 前端定時刷新 (jQuery)
setInterval(function() {
$('#heartbeat').attr('src', '/keepalive.ashx?t=' + Date.now());
}, 10000); // 10秒
缺點:併發大時產生大量無意義請求,增加服務器壓力。
// 登錄時設置
HttpCookie cookie = new HttpCookie("AuthToken", token);
cookie.HttpOnly = true; // 防 XSS
cookie.Secure = true; // 僅 HTTPS
// 不設置 Expires → 瀏覽器進程結束自動刪除
Response.Cookies.Add(cookie);
局限:用戶禁用 Cookie 失效;多標籤頁共享同一 Session Cookie,關閉一個不影響其它。
window.addEventListener('beforeunload', function() {
// 同步請求確保發送完成(異步可能被瀏覽器取消)
navigator.sendBeacon('/logout.ashx'); // 現代 API,可靠異步
// 或傳統同步 AJAX
// $.ajax({url:'/logout.ashx', async:false});
});
致命缺陷:用戶開多個標籤頁,關閉任一標籤即觸發登出,體驗極差。
// 1. 每次請求更新時間戳 (全局 Filter/中間件)
public class ActivityFilter : IActionFilter {
public void OnActionExecuting(ActionExecutingContext context) {
var session = context.HttpContext.Session;
session.SetString("LastActivity", DateTimeOffset.UtcNow.ToString("o"));
}
}
// 2. 後台服務定期掃描 (Quartz.NET / IHostedService)
public class SessionCleanupService : BackgroundService {
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
await CleanupExpiredSessions();
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken); // 5分鐘掃描一次
}
}
private async Task CleanupExpiredSessions() {
var cutoff = DateTimeOffset.UtcNow.AddMinutes(-20); // 20分鐘無活動視為過期
// 從 Session 存儲 (Redis/SQL/文件) 刪除 LastActivity < cutoff 的會話
await sessionStore.RemoveExpired(cutoff);
}
}
優勢:準確反映用戶真實活躍狀態,不受瀏覽器行為影響,支持分布式部署(共享 Session 存儲)。
// web.config (ASP.NET)
<sessionState timeout="20" /> // 20分鐘滑動過期
// ASP.NET Core
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(20);
});
零代碼成本,但 Session 滯留佔用內存/存儲,高併發下資源浪費明顯。
beforeunload 確認對話框,減少誤關閉JWT 無狀態認證 + 短過期 Access Token + 長過期 Refresh Token 徹底繞過 Session 問題:
- Access Token 5-15 分鐘自動過期,無需服務端存儲
- Refresh Token 存儲於 HttpOnly Cookie,攜帶指紋/設備信息
- 瀏覽器關閉 → Access Token 失效 → 下次訪問自動刷新或重新登錄
- 服務端維護 Refresh Token 撤銷列表(Redis Set),登出即加入黑名單
這是當前主流單頁應用、微服務架構的標準認證模式,徹底解決 Session 滯留、分布式共享、CSRF 等歷史難題。