很多系統要求防止用戶被重複登陸,我們通常思維是當有用戶重複登陸時要禁止他再登陸進去,
提示他該賬號已經有人在使用中.
然而這樣有個問題很難解決,就是系統很難實時捕捉到該賬號是否還在使用中,
如當用戶非正常退出或者遇到停電等,系統只好等到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並不會太大影響系統的效率,這樣可以“實時”地檢測到用戶是否重複登陸