Asp.Net的Forms驗證,解決Cookie和Seesion失效時間

  網站開發中用戶驗證一般採用Asp.Net的Forms驗證,驗證票據存儲到Cookie的方式。

    Session方式是將驗證信息存儲在內存中,如果你使用的虛擬主機給你分配很小的內存,實際上都是如此,那麼session就會很快過期,要求你重新登錄,如果用戶正在填寫信息,被要求重新登錄,那憤怒的感覺可想而知。

   cookie是存儲在用戶的客戶端的。但是也會碰到失效的問題,下面一一來了解。

    在ASP.NET Forms驗證中,通常我們會使用ASP.NET自帶的Login控件來進行驗證。同時,在web.config文件中,我們所有的Forms設置都設爲默認。現在,問題就來了。

1.爲什麼我明明點了"Remember me",而大概半個小時後,我又Log out了?

2.爲什麼我明明設置了timeout爲無限期 e.g. 400000,爲什麼一兩天之後我又Log out了呢?

這是Forms驗證中遇到的比較多的問題。下面,我就這兩個問題做一個詳細的解釋:對於問題一,首先我要闡明ticket和cookie的區別。 cookie是一個容器,用來存放東西,它是保存在客戶端的。而ticket是具體的數據,用來表示具體的驗證信息,它是放在cookie這個容器中的。 因而,在我們驗證的過程中,以下事情發生了。首先,ticket被創造了,裏面包含着用戶名等信息,同時它有一個過期時間。

    然後,cookie被創造了,它同樣也有一個過期時間。最後,將ticket保存在cookie中,並將此cookie發送到client的瀏覽器中。讀 到這裏,我想問題已經很明白了,用戶的Log out就是因爲時間過期的問題。但具體是誰的時間過期了呢?在我們ASP.NET web.config的設置中,timeout是cookie的過期時間(注意,默認是30分鐘),而ticket的過期時間是無限的,因爲我們選 了"Remember me".這就是爲什麼雖然我點了"Remember me"。

    但在30分鐘左右後,我仍然被Log out了,因爲我們並沒有設置cookie的timeout.ticket和cookie,只要其中之一不是永遠不過期,我們都無法實現永不過期。  

    當我們解決了問題一後(假如手動設置timeout="4000000"),我們又遇到了問題二。這又是什麼原因呢?這得從ticket的加密解密機制說 起。ASP.NET會使用一個machinekey來對cookie進行加密,這個machinekey默認是在application啓動時隨機生成 的。然後,ASP.NET會使用同一個machinekey進行cookie進行解密。正式因爲這個key是application啓動時隨機生成的才導 致了問題二。試想,如果application recycle(重啓)了怎麼辦?

ASP.NET會生成另一個key進行解密,以前的cookie將不再有效,這就是問題二的原因了。知道了這個,解決第二個問題的辦法就很簡單了, 手動設置一個特定的key.如:<machineKey validationKey="88CB6CA6CF403C5FBB41C2F62BB7FCFCA05DE7BE" decryptionKey="B8A7CF3816C57176" validation="SHA1" />

實現Asp.net Forms身份驗證的操作步驟

 

對於應用程序的身份驗證,一直是自已編寫登陸窗體,在窗體的CS文件中判斷用戶的登錄 是否合法,如果合法則將用戶名保存在Cookie中。然後將所有頁面的繼承於一個類似BaseForm這樣的基頁面,在這個頁面的Page_Load事件 中加入判斷,根據Cookie來判斷用戶是否已登錄,如果沒有登錄則跳轉到登錄頁面。
最近作一個互連網網站,想起安全性的問題,查閱了一些資料後覺得采用Asp.net提供的標準的Forms驗證方式。研究了一下,現在寫出操作步驟,以供以後使用的時候參考(博客現在的一個很重要的功能就是用來保存曾經的學習積累)
1、 修改web.config文件。如果vs2005的話,默認沒有這個文件,調試時纔會提示你是否要添加該文件。在web.config文件的<system.web>節中添加下面的三部分內容:
<authentication mode="Forms">
       <forms loginUrl="default.aspx" name=".ASPXFORMSAUTH">
       </forms>
     </authentication>
     <authorization>
       <deny users="?" />
     </authorization>
    <machineKey
    validationKey="AutoGenerate,IsolateApps"
    decryptionKey="AutoGenerate,IsolateApps"    
    validation="SHA1"
    decryption="Auto" />
上面三部分分別標有不同顏色,第一部分是設定應用程序的身份驗證模式,默認是 windows,如果你的系統是在局域網內使用,並且整個局域網是工作在域模式下,那windows身份驗證方式將會有很好的效果,如果多個B/S用 windows模式來進行身份驗證,那麼他們甚至可以非常方便的就實現了單點登陸的效果。但Forms身份驗證使用的更普遍些,雖然相對來說他存在安全性 差的問題。在authentication節下有個forms節,在那裏你可以填上執行你身份驗證的頁面,以及存儲身份驗證所採用的Cookie名稱。如 果你不填寫Cookie名稱的話,Asp.net默認分配.ASPXFORMSAUTH作爲Cookie名稱。
     第二部分authorization 是授權部分。你可以配置允許或拒絕某些用戶或者角色來訪問你的應用程序,他下面有deny,allow可以使用,搭配通配符?、*讓我們可以非常輕易的配置出輕量級簡單的身份驗證體系。詳細的配置信息可以參照asp.net的幫助文檔,在此不在詳細描述。
     第三部分 machineKey 是用來配置存取Cookie時採用的加密/解密算法。有了這個算法後,Cookie應該算是比較安全了吧。
2、 將Cookie信息登錄後保存起來。
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket (1,CookieInfo , DateTime .Now, DateTime .Now.AddHours(20),false ,UserData ); // User data
        string encryptedTicket = FormsAuthentication .Encrypt(authTicket); // 加密
        //    存入Cookie
        HttpCookie authCookie = new HttpCookie (FormsAuthentication .FormsCookieName,encryptedTicket);
         authCookie.Expires = authTicket.Expiration;
         Response.Cookies.Add(authCookie);
首先創建一個身份票據,身份票據主要保存用戶的名稱,Cookie的過期時間,另外你還 可以保存一些額外數據,比如用戶所扮演的角色。這裏有個地方要注意,UserData是用來保存一些額外數據,比如角色,但如果我沒有數據的話,通常會傳 一個null值進去,但如果你是想將Cookie信息進行加密保存的話,這裏不能傳null值,你可以傳一個””值。如果傳null值的話,在執行string encryptedTicket = FormsAuthentication .Encrypt(authTicket); 的時候會返回一個null值,以至於生成一個錯誤的加密Cookie值。如果要將Cookie進行長期保存,需要爲Cookie設置一個過期時間。
3、 讀取保存在計算機中的Cookie。
在Global.asax 文件中存在Application_AuthenticateRequest事件,它是執行所有服務器端請求的時候執行。因此我們可以在這個地方讀取Cookie並進行解密。以下是示例代碼:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
     {
        string cookieName = FormsAuthentication .FormsCookieName;// 從驗證票據獲取Cookie的名字。
        // 取得Cookie.
        HttpCookie authCookie = Context.Request.Cookies[cookieName];
        if (null == authCookie)
            return ;
        FormsAuthenticationTicket authTicket = null ;
        // 獲取驗證票據。
         authTicket = FormsAuthentication .Decrypt(authCookie.Value);
        if (null == authTicket)
            return ;
       
        // 驗證票據的UserData中存放的是用戶角色信息。
        //UserData 本來存放用戶自定義信息。此處用來存放用戶角色。
        string [] roles = authTicket.UserData.Split(new char [] { ',' });
        FormsIdentity id = new FormsIdentity (authTicket);
        GenericPrincipal principal = new GenericPrincipal (id, roles);
        // 把生成的驗證票信息和角色信息賦給當前用戶.
         Context.User = principal;
     }
當你對Cookie加密存儲之後必須要進行解密後才能進行使用。
4、 在每個頁面讀取Cookie的值。可以通過下面的語句來讀取Cookie的值。HttpContext .Current.User.Identity.Name



在項目中用到的驗證(並沒有實現cookis不過期):
  public class  LoginUserManager : ILoginUserManager
    {
        private IUserService userService;

        public IUserService UserService
        {
            set { userService = value; }
        }

        public void MarkCurrentUser(User user, bool isPersistent)
        {
            string mUserData = string.Format("{0};{1};{2}", user.Account, user.UserName, user.EmployeeNO);
            SaveToCookies(user.Account, mUserData, isPersistent);
        }

        public User CurrentUser(bool fullData)
        {
            User member = GetFromCookies<User>();
            if (fullData && member != null && !string.IsNullOrEmpty(member.Account))
            {
                return userService.GetObjectById(null, member.Account);
            }
            return member;
        }

        private static T GetFromCookies<T>() where T : class
        {
            if (HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated
            && (HttpContext.Current.User.Identity is FormsIdentity))
            {
                HttpCookie myCookie = HttpContext.Current.Request.Cookies["ud"];
                if (myCookie != null && !string.IsNullOrEmpty(myCookie.Value))
                {
                    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(myCookie.Value);
                    if (ticket != null)
                    {
                        string[] userInfoArray = ticket.UserData.Split(';');
                        //UserData format like as "loginName;password;role1,role2,role3..."
                        if (userInfoArray.Length == 3 && typeof(T).Name == "User")
                        {
                            User member = new User();
                            member.Account = userInfoArray[0];
                            member.UserName = userInfoArray[1];
                            member.EmployeeNO = userInfoArray[2];
                            return member as T;
                        }
                    }
                }
            }
            return default(T);
        }

        private static void SaveToCookies(string name, string mUserData, bool isPersistent)
        {
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
                1,
                name,
                DateTime.Now,
                DateTime.Now.AddDays(1),
                isPersistent,
                mUserData);
            string hashTicket = FormsAuthentication.Encrypt(ticket);
            HttpCookie cookie = new HttpCookie("ud", hashTicket);
            if (HttpContext.Current.Response.Cookies["ud"] != null)
            {
                HttpContext.Current.Response.Cookies["ud"].Expires = DateTime.Now.AddDays(-1d);
            }
            HttpContext.Current.Response.Cookies.Add(cookie);
        }
    }


發佈了14 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章