最近開始接觸asp.net,開發一個線上考試系統,其中涉及到使用者管理和許可權控制,要求如下:
1. 使用者需要經過驗證方能進入系統;
2. 不同層級的使用者登入後看到的內容不同;
3. 管理員可進行管理操作,如角色、使用者、許可權的增刪改查;
4. 防止非系統使用者直接在地址欄輸入某一頁面地址進入系統,從而繞過登入頁面;
5. 防止低許可權使用者登入系統後在地址欄輸入高許可權使用者才能訪問的某一頁面地址,從而實現越權訪問;
6. 同一使用者多地登入,則後登入的擠掉先登入的,類似 QQ
對於第2個問題可以採用重新導向的方法來解決
在網站建立Admin和Tester兩個檔案夾,分別存放管理員和考試人員使用的檔案,然後在登入介面進行重新導向。
//返回重新導向URL string returnUrl = FormsAuthentication.GetRedirectUrl(loginUser.UserID, false); if (returnUrl.Equals(FormsAuthentication.DefaultUrl)) { //根據角色,確定跳轉頁面 if (RoleCheck.IsAdmin()) returnUrl = "~/Admin/Default.aspx"; else returnUrl = "~/Tester/Default.aspx"; } //重新定向到指定的URL頁面 Response.Redirect(returnUrl);
對於第4個問題可以採用Session來實現,在本項目中建立一個SessionClass類,在裡面實現兩個擷取和設定UserID的方法
public static Entity.User GetLoginUser() { if (null == HttpContext.Current.Session["loginUser"]) return null; return (Entity.User)Tool.SerializeHelper.DeserializeObject(HttpContext.Current.Session["loginUser"].ToString()); } public static void SetLoinUser(Entity.User loginUser) { if(loginUser!=null) HttpContext.Current.Session["loginUser"] =Tool.SerializeHelper.SerializeObject(loginUser); }
對於第5個問題,本項目建立一個RoleCheck類,對登入人員進行驗證
public static class RoleCheck{ private const string ADMIN_ROLE = "管理員"; public static bool IsAdmin() { Entity.User user=SessionClass.GetLoginUser(); if (user != null) { foreach (string right in user.RightList) { if(ADMIN_ROLE.Equals(right.Trim())) return true; } } return false; } /// <summary> /// 拒絕非管理使用者的登入,拒絕後將跳轉到登陸頁面 /// </summary> public static void DenyLogin() { if (!IsAdmin()) HttpContext.Current.Response.Redirect(string.Format("~/LoginSystem.aspx?errorMsg={0}","您的許可權不是管理員,無法訪問該頁面!(也有可能是登入逾時,請重新登入)")); }}
對於最後一個問題,通過SessionID和使用者名稱來保證同一使用者不能同時登入。步驟如下:
1.採用Application全域變數來儲存SessionID和使用者名稱,每次登入時都儲存,並且將Application儲存到Hashtable中,當使用者登入成功後,首先判斷該使用者是否已經儲存在Application中,如果存在(說明已經登入),則將該使用者對應的值設定為XX。
protected void loginbtn_Click(object sender, EventArgs e) { //登入成功。。。。。。。 Hashtable hOnline = (Hashtable)Application["Online"];// 讀取全域變數 if (hOnline != null) { IDictionaryEnumerator idE = hOnline.GetEnumerator(); string strKey = ""; while (idE.MoveNext()) { if (idE.Value != null && idE.Value.ToString().Equals(UserID))//如果目前使用者已經登入, { //already login strKey = idE.Key.ToString(); hOnline[strKey] = "XX";//將當前用 戶已經在全域變數中的值設定為XX break; } } } else { hOnline = new Hashtable(); } hOnline[Session.SessionID] = UserID;//初始化目前使用者的 Application.Lock(); Application["Online"] = hOnline; Application.UnLock(); Response.Redirect("main.aspx"); }
2,寫一個BasePage加一個Init方法如下,系統的所有頁面(除了登入和退出頁面)均繼承自該BasePage類
protected override void OnInit(EventArgs e) { Hashtable hOnline = (Hashtable)Application["Online"];//擷取已經儲存的application值 if(hOnline != null) { IDictionaryEnumerator idE = hOnline.GetEnumerator(); while(idE.MoveNext()) { if(idE.Key != null && idE.Key.ToString().Equals(Session.SessionID)) { if (idE.Value != null && "XX".Equals(idE.Value.ToString()))//說明在別處登入 { hOnline.Remove(Session.SessionID); Application.Lock(); Application["Online"] = hOnline; Application.UnLock(); Response.Write("script type="text/javascript"> alert('你的帳號已在別處登陸,你被強迫下線!');top.location.href='Default.aspx';window.close();</script>"); Response.Redirect("Default.aspx"); Response.End(); } } } }
3.在程式退出後從Application中清除當前SessionID
void Session_End(object sender, EventArgs e) { // 在會話結束時啟動並執行代碼。 // 注意: 只有在 Web.config 檔案中的 sessionstate 模式設定為 // InProc 時,才會引發 Session_End 事件。如果會話模式設定為 StateServer // 或 SQLServer,則不會引發該事件。 Hashtable hOnline = (Hashtable)Application["Online"]; if (hOnline[Session.SessionID] != null) { hOnline.Remove(Session.SessionID);//清除當前SessionID Application.Lock(); Application["Online"] = hOnline; Application.UnLock(); } }
4.如果程式非正常退出,SessionID沒有及時的清除,那麼也不會影響帳號的正常登入,而SessionID也會隨著Session的到期而自動清除,伺服器也不會有壓力。
5,如果感覺儲存在Application中感覺不好,也可以將SessionID存入資料庫中,判斷方法和上面一樣。