很多系統要求防止用戶被重復登陸,我們通常思維是當有用戶重復登陸時要禁止他再登陸進去,
提示他該賬號已經有人在使用中.
然而這樣有個問題很難解決,就是系統很難實時捕捉到該賬號是否還在使用中,
如當用戶非正常退出或者遇到停電等,系統只好等到Session超時后才能知道該賬號已經下線.
在Session超時之前這段時間之內沒有人使用賬號但也沒人再能登陸上去,只能干等着了.
我的解決方法是模仿QQ的被迫下線的功能。只要你QQ號和密碼正確,隨時都可以登陸上去,但是如果
該QQ號此前有人在使用的話那先前的人就會被擠下去。我們這樣來實現系統防止用戶重復登陸的話就可以輕松實現。關鍵就是在Application或數據庫中記錄下在線用戶的SessionID,檢測使用者的SessionID是否和Application中記錄的該用戶ID相對應的SessionID相同,若不相同則提示下線!
用戶登陸成功時用戶ID和SessionID寫入Application中。
public void WriteToApplication( string UserID ) { DataTable dtUserFlagList = new DataTable(); try { dtUserFlagList = (DataTable)Application["UserFlagList"]; } catch { dtUserFlagList = null; } if( dtUserFlagList == null ) { DataColumn dc0 = new DataColumn("UserID",typeof(System.String)); DataColumn dc1 = new DataColumn("SessionID",typeof(System.String)); dtUserFlagList.Columns.Add( dc0 ); dtUserFlagList.Columns.Add( dc1 ); dtUserFlagList.Columns.Add( dc2 ); DataRow drUser = dtUserFlagList.NewRow(); drUser["UserID"] = UserID; //寫入用戶ID drUser["SessionID"] = Session.SessionID;//記錄下SessionID dtUserFlagList.Rows.Add( drUser ); } else { int i = 1; foreach( DataRow drUser in dtUserFlagList ) { if( drUser["UserID"].ToString() == UserID ) { drUser["SessionID"] = Session.SessionID;//如果在Application中存在當前用戶ID,則把記錄的SessionID值改為當前的SessionID break; } i++; } if( i > dtUserFlagList.Rows.Count ) { DataRow drUser = dtUserFlagList.NewRow(); drUser["UserID"] = UserID; //寫入用戶ID drUser["SessionID"] = Session.SessionID;//記錄下SessionID dtUserFlagList.Rows.Add( drUser ); } } Application.Lock(); Application["UserFlagList"] = dtUserFlagList; //記錄到Application中 Application.UnLock(); Session["UserID"] = UserID;//把相關登陸信息寫入Session }
下一步我們只要檢測Application中記錄的SessionID是否和當前的SessionID相同就可以了
我在這里寫了一個頁面的基類,每個頁面繼承這個類。這個類改寫OnLoad()事件,加入檢測SessionID值
public class PageBase : System.Web.UI.Page { public PageBase() { // // TODO: 在此處添加構造函數邏輯 // } protected override void OnLoad(EventArgs e) { base.OnLoad (e); if( ! IsPostBack ) { if( Session["UserID"] == null ) { Response.Redirect("Login.aspx");//沒有登陸 return; } DataTable dtUserFlagList = (DataTable)Application["UserFlagList"]; foreach( DataRow drUser in dtUserFlagList ) { if( drUser["UserID"].ToString() == Session["UserID"].ToString() ) { if( drUser["SessionID"].ToString() == Session.SessionID ) break;//SessionID值相同,沒有其他用戶登陸 else { Session.Abandon();//SessionID不相同,有人在使用該賬號,被迫下線 Response.Redirect("ShowError.aspx?action=該賬號已在別處登陸",false); } } } } } }
當用戶正常退出系統或非正常退出Session超時,在Application中清除該賬號的記錄
以下代碼寫在Global.asax.cs文件Session_End方法中
protected void Session_End(Object sender, EventArgs e) { if( Session["UserID"] == null ) return; DataTable dtUserFlagList = (DataTable)Application["UserFlagList"]; foreach( DataRow drUser in dtUserFlagList ) { if( drUser["UserID"].ToString() == Session["UserID"].ToString() && drUser["SessionID"].ToString() == Session.SessionID ) { //當UserID和SessionID都和退出用戶的值相等時才清除記錄。 drUser.Delete(); break; } } dtUserFlagList.AcceptChanges(); Application.Lock(); Application["UserFlagList"] = dtUserFlagList; //將更改記錄到Application中 Application.UnLock(); }
以上方法已經在我的系統中實現,在我的系統可以設定某個賬號同時登陸多少人,只是在記錄中多了一個字段amount。
這個方法可能不好的地方就是每訪問一個頁面都要檢測是否SessionID值相等,
不過我覺得讀取Application並不會太大影響系統的效率,這樣可以“實時”地檢測到用戶是否重復登陸
摘自:http://hi.baidu.com/netidea/item/65296b3b9778a1fe96f88dd5