1. 程式人生 > >ASP.NET防使用者重複登入的方法

ASP.NET防使用者重複登入的方法

本例完成的功能就是防止使用者重複登入!若使用者已經登入,則當其再次登入時,彈出提示框後返回!

實現思路:
使用者登入成功後,將使用者登入資訊存放到Hashtable型別的Application[“Online”]裡面,其鍵值為SessionID,其Value值為使用者ID;當用戶登出時,呼叫Session.Abandon;在Global.asax裡面的SessionEnd事件中,將使用者ID從Hashtable中刪除;在使用者訪問頁面時,察看Hashtable中是否有對應的使用者ID如果沒有則判斷使用者不線上(使用者不線上的原因可能是按了登出按鈕、網頁超時等)

1、公用類中判斷使用者是否線上的函式(供使用者呼叫)

 /**//// <summary> 
 /// 判斷使用者strUserID是否包含在Hashtable h中 
 /// </summary> 
 /// <param name="strUserID"></param> 
 /// <param name="h"></param> 
 /// <returns></returns> 
 public static bool AmIOnline(string strUserID, Hashtable h)
 {
     if (strUserID == null)
        return
false; //繼續判斷是否該使用者已經登陸 if (h == null) return false; //判斷雜湊表中是否有該使用者 IDictionaryEnumerator e1 = h.GetEnumerator(); bool flag = false; while (e1.MoveNext()) { if (e1.Value.ToString().CompareTo(strUserID) == 0) { flag = true; break
; } } return flag; }

2、使用者登入事件處理:

private void btnlogin_Click(object sender, System.Web.UI.ImageClickEventArgs e)
{ 
    //User為自定義的類,其中包含Login方法
    User CurUser = new User();
    CurUser.UserID = this.username.Text.Trim();

    if (MyUtility.AmIOnline(CurUser.UserID, (Hashtable) Application["Online"]))
    {
        JScript.Alert("您所使用的登入ID已經線上了!您不能重複登入!");
        return;
    }

    CurUser.LoginPsw = FormsAuthentication.HashPasswordForStoringInConfigFile(this.password.Text.Trim(), "SHA1");
    int ii = CurUser.Login();
    StringBuilder sbPmt = new StringBuilder();

    switch (ii)
    {
    case 0: //如果登入成功,則將UserID加入Application["Online"]中
        Hashtable h = (Hashtable) Application["Online"];
        if (h == null)
            h = new Hashtable();
        h[Session.SessionID] = CurUser.UserID;
        Application["Online"] = h;

        Session["UserID"] = CurUser.UserID;
        Session["UserNM"] = CurUser.UserNM;
        Session["RoleMap"] = CurUser.RoleMap;
        Session["LoginPsw"] = CurUser.LoginPsw;
        Session["LoginTime"] = DateTime.Now;
        Response.Redirect("ChooseRole.aspx");
        break;
    case -1:
        JScript.Alert("使用者名稱錯誤!");
        break;
    case -2:
        JScript.Alert("密碼錯誤!");
        break;
    default:
        sbPmt.Append("登入過程中發生未知錯誤!");
        JScript.Alert(sbPmt.ToString());
        break;
    }
    return;
}

3、在Global.asax中的Session_End事件:

protected void Session_End(Object sender, EventArgs e)
{
    Hashtable h = (Hashtable) Application["Online"];

    if (h[Session.SessionID] != null)
        h.Remove(Session.SessionID);

    Application["Online"] = h;
}

4、在每一個頁面需要重新整理的地方,呼叫如下程式碼:

try
{
    if (!common.MyUtility.AmIOnline(Session["UserID"].ToString(), (Hashtable) Application["OnLine"]))
    {
        //使用者沒有線上 ,轉到登入介面
        Response.Write("<script>parent.document.location.href='Login.aspx';</script>"); ////有框架時用
        //Response.Redirect("login.aspx"); ////無框架時用
        return;
    }
}
catch
{
    //會話過期 ,轉到登入介面
    Response.Write("<script>parent.document.location.href='Login.aspx';</script>"); ////有框架時所用
    //Response.Redirect("login.aspx"); ////無框架時用
    return;
}

深入思考:

由本例的解決方法可以加以延伸,比如,在儲存UserID的時候,將UserID+客戶端IP地址一起存進去,

則在將相應資訊取出來分析的時候,可以做到:當用戶在不同的計算機上先後登入的時候,則允許最近一次的登入,而將之前的登入刪除!等等

以上方法對於正常退出的檢測是可行的,但是對於非正常退出的方法不行。
非正常退出包括 ,直接關閉 瀏覽器 。這時候 Global.asax中的Session_End 的事件就不會被觸發,由於Session值的消失要20分鐘左右,因此在這20分鐘內,系統無法判斷客戶端是否已經退出,只能作為使用者仍然線上來對待。也就是說,這20分鐘內,該使用者無法再次登入,必須等20分鐘後,方可正常登入。
解決方案查到如下三種
一 : 後登陸的踢出先登入的。
在 Application 中儲存線上使用者時,也儲存一下登入時刻 ,當 後期有使用者登入時 ,就更新登入時刻 ,若 之前使用者 比較時 發現 自己的登入時刻 跟 儲存的 不一致 時 ,將提示 已被踢出。
二 : 使用Javascript方式
在每一個頁面中加入一段javascript程式碼:

function window.onbeforeunload() 
  { 
      if (event.clientX>document.body.clientWidth && event.clientY<0||event.altKey){ 
      window.open("logout.aspx"); 
      } 
  }

 由於onbeforeunload方法在瀏覽器關閉、重新整理、頁面調轉等情況下都會被執行,所以需要判斷是點選了關閉按鈕或是按下Alt+F4時才執行真正的關閉操作。

  然後在logout.aspx的Page_Load中寫和Session_End相同的方法,同時在logout.aspx中加入事件:onload=”javascript:window.close()”

  但是這樣還是有問題,javascript在不同的瀏覽器中可能有不同的行為,還有就是當通過檔案->關閉時沒有判斷到。
三: 使用xmlhttp方法(這種方法測試下來沒有問題)
 在每個頁面中加入如下的javascript(這些javascript也可以寫在共通裡,每個頁面引入就可以了)
 

var x=0; 
  function myRefresh() 
  { 
      var httpRequest = new ActiveXObject("microsoft.xmlhttp"); 
      httpRequest.open("GET", "test.aspx", false); 
      httpRequest.send(null); 
      x++; 
      if(x<60) //60次,也就是Session真正的過期時間是30分鐘 
      { 
      setTimeout("myRefresh()",30*1000); //30秒 
      } 
  } 
  myRefresh();

  在web.config中設定
  

<sessionState mode="InProc" timeout="1"></sessionState>

test.aspx頁面就是一個空頁面,只不過需要在Page_Load中加入:

 Response.Expires = -1;

 保證不使用快取,每次都能呼叫到這個頁面。

  原理就是:設定Session的過期時間是一分鐘,然後在每個頁面上定時每30秒連線一次測試頁面,保持Session有效,總共連60次,也就是30分鐘。如果30分鐘後用戶還沒有操作,Session就會過期。當然,如果使用者直接關閉瀏覽器,那麼一分鐘後Session也會過期。這樣就可以滿足要求了。
  但是 缺點很明顯。