業務常見需求:同一賬號同一時間僅允許一個會話在線,新登錄擠掉舊會話或拒絕新登錄。本文提供兩種實現思路,均利用服務端全局狀態存儲。
// 登錄頁邏輯
string sKey = username.Text.Trim();
string cachedUser = Cache[sKey]?.ToString();
if (string.IsNullOrEmpty(cachedUser))
{
// 首次登錄:寫入 Cache,過期時間同步 Session Timeout
TimeSpan timeout = TimeSpan.FromMinutes(Session.Timeout);
Cache.Insert(sKey, sKey, null, DateTime.MaxValue, timeout,
CacheItemPriority.NotRemovable, null);
Session["User"] = sKey; // 正常登錄流程
}
else if (cachedUser == sKey)
{
// 已在線:提示並阻止
ClientScript.RegisterStartupScript(GetType(), "alert",
"alert('該賬號已在其他地方登錄');", true);
return;
}
else
{
// 異常狀態:清理 Session 重試
Session.Abandon();
}
關鍵:客戶端關閉瀏覽器時清理 Cache
// 頁面 onUnload 事件 (需考慮瀏覽器兼容性)
window.onunload = function() {
$.ajax({
type: "POST",
url: "ClearCache.aspx",
data: { sKey: "<%= sKey %>" },
async: false // 同步請求確保發送完成
});
};
// ClearCache.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
string key = Request.Form["sKey"];
if (!string.IsNullOrEmpty(key)) Cache.Remove(key);
}
// Global.asax Session_End 兜底清理
void Session_End(object sender, EventArgs e)
{
Hashtable online = (Hashtable)Application["Online"];
if (online?[Session.SessionID] != null)
{
online.Remove(Session.SessionID);
Application.Lock();
Application["Online"] = online;
Application.UnLock();
}
}
// 登錄檢查
public bool CheckOnline(string userName)
{
Hashtable online = (Hashtable)Application["Online"] ?? new Hashtable();
foreach (DictionaryEntry entry in online)
{
if (entry.Value?.ToString() == userName) return false; // 已在線
}
return true; // 可登錄
}
// 登錄成功後註冊
Hashtable online = (Hashtable)Application["Online"] ?? new Hashtable();
online[Session.SessionID] = userName;
Application.Lock();
Application["Online"] = online;
Application.UnLock();
// 頁面卸載/Session_End 移除邏輯同上
| 維度 | Cache 方案 | Application 方案 |
|---|---|---|
| 性能 | 高 (內建過期掃描) | 需手動清理過期項 |
| 可靠性 | 依賴瀏覽器 onUnload (不可靠) | 依賴 Session_End (InProc 模式可靠) |
| 擴展性 | 易遷移至 Redis 分布式 | 單機綁定,難擴展 |
| 實現複雜度 | 低 | 中 |
AccessToken 存入 Redis (Key: user:{uid}:token),值含 SessionID、登錄時間、IP。新登錄檢查 Key 是否存在,決定擠下線或拒絕。上述兩種方案適合單機/小規模部署,理解其核心邏輯「全局唯一標識 + 生命週期管理」有助於向分布式方案演進。