手動類比Session機制的原理

來源:互聯網
上載者:User

 

  概要的說,.NET內建的Session其實是藉助於Cookie機制實現的。下面先解釋為什麼這麼說,然後通過對比一個.NET內建的Session執行個體和一個手動類比的Session機制(會藉助於Cookie)來進一步驗證。


為什麼這麼說

(1)首先,由於需要用到一些必要的Cookie相關知識,這裡簡要介紹:

  Ø  Cookie存放於用戶端,所以東西是在別人手中,所以就不會很安全

  Ø  由於瀏覽器可以設定清除、禁用Cookie,所以不可以將不可或缺的東西放到Cookie中,只能將可有可無的東西放到Cookie中。

  Ø  伺服器將Cookie寫到用戶端,之後當客戶訪問伺服器(瀏覽器不變)該網站的任何內容時,request的資訊中都會帶上所有的Cookie的值。

  Ø  Cookie不能誇不同品牌的瀏覽器,互相訪問(比如Chrom寫的Cookie,IE不能使用)。

  Ø  若不設定Cookie到期時間(Expires),則稱為會話Cookie,表示這個Cookie的生命期為瀏覽器會話期間,關閉瀏覽器視窗,Cookie就消失,會話Cookie一般不儲存在硬碟上而是儲存在記憶體裡;若設定了到期時間,瀏覽器就會把Cookie儲存到硬碟上,關閉後再次開啟瀏覽器,這些Cookie仍然有效直到超過設定的到期時間,或許應該這麼說,如果設定了Expires,則這個Cookie的最長生命週期為Expires的值,如果Cookie已存滿(個數和容量大小都有限制)則即使沒到期,瀏覽器也會給刪除。

(2)下面開始解釋.NET的Session存放原理:

  上面介紹了,ASP.NET為了保持和傳遞狀態,一種機制就是上面的Cookie。寫入Cookie的資料是存放在用戶端的,不會很安全,所以需要將資料從用戶端移到伺服器端,即所謂的Session。但Session並不是將單純的資料直接儲存到伺服器記憶體中,而是建立一個類似散列表的id-value映射表結構,value即指我們要儲存的資料,id是由ASP.NET自動產生的一個編號(ASP.NET_SessionId),即:每當我們要寫資料到Session時,ASP.NET就會產生一個編號(ASP.NET_SessionId,它是一個既不會重複,又不容易被找到規律以仿造的字串),然後將編號作為資料的索引把二者進行綁定,然後會將這個編號以Cookie的形式儲存到用戶端,而將資料保留到伺服器端的記憶體中,這樣在用戶端請求Session的同時會將Cookie中的ASP.NET_SessionId一同發送到伺服器端,伺服器就可以根據這個編號在自己的資料中進行檢索,進而取出它的value值。這就是ASP.NET中的Session機制原理。可參考下面的草圖:


  


執行個體說明-分析

  下面做這麼一個小例子:在登陸的時候將使用者名稱寫入Session,然後才能瀏覽網站內部的其它頁面。如果沒有登入,直接請求網站內部的page,則會檢測Session,如果為空白則跳轉到登陸頁面。

  分別用.NET內建的Session執行個體和手動類比的Session機制(會藉助於Cookie)來實現。

  當然,處理頁面的很少的部分仍然採用NVelocity,所以首先添加它的DLL,並引用(參考前面兩篇文章)。然後添加封裝好的”渲染”代碼, 如下的CommonHelper類:

    public class CommonHelper    {        /// <summary>        /// 用data資料填充templateName模板,渲染產生html,返回        /// </summary>        /// <param name="templateName"></param>        /// <param name="data"></param>        /// <returns></returns>        public static string RenderHtml(string templateName, object data)        {            VelocityEngine vltEngine = new VelocityEngine();            vltEngine.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file");            vltEngine.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, System.Web.Hosting.HostingEnvironment.MapPath("~/templates"));//模板檔案所在的檔案夾            vltEngine.Init();            VelocityContext vltContext = new VelocityContext();            vltContext.Put("TemData", data);//設定參數,在模板中可以通過$data來引用            Template vltTemplate = vltEngine.GetTemplate(templateName);            System.IO.StringWriter vltWriter = new System.IO.StringWriter();            vltTemplate.Merge(vltContext, vltWriter);            string html = vltWriter.GetStringBuilder().ToString();            return html;        }    }
  .NET內建的Session執行個體

    先用我們平時用的Session實現上例,看看它的儲存機制。

    添加三個檔案:Login3.html、Login3.ashx(做登入處理);TestLogin3.ashx(作為網站內部的其它頁面)。代碼如下:

    Login3.html源碼及運行介面:   

<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>    <title>.net內建的Session測試</title></head><body>    <form action="../Login3.ashx" method="get">        使用者名稱:<input type="text" name="username" />        <br />        密 碼:<input type="text" name="password"/>        <br />        <input type="submit" name="login" value="提交" />    </form></body></html>




    Login3.ashx:    

    public class Login3 : IHttpHandler, IRequiresSessionState    {        public void ProcessRequest(HttpContext context)        {            context.Response.ContentType = "text/html";            string UserName = context.Request["username"];            string PassWord = context.Request["password"];            //登陸時,判斷使用者名稱是否正確(這裡設定為admin),正確則儲存使用者名稱到Session,並跳轉到TestLogin3.ashx            //否則清空輸入框,頁面不變            if (UserName == "admin")            {                //將使用者名稱儲存到.net內建Session中                context.Session["username"] = UserName;                //儲存好使用者名稱後進入TestLogin3.ashx頁面                context.Response.Redirect("TestLogin3.ashx");            }            else            {                string html = CommonHelper.RenderHtml("Login3.html", null);                context.Response.Write(html);            }        }        public bool IsReusable        {            get            {                return false;            }        }    }

   

   TestLogin3.ashx:    

   public class TestLogin3 : IHttpHandler,IRequiresSessionState    {        public void ProcessRequest(HttpContext context)        {            context.Response.ContentType = "text/html";            //如果Session為null,則跳轉到登陸頁            if (context.Session == null)            {                context.Response.Redirect("Login3.ashx");            }            else            {                 string username=(string)context.Session["username"];                //如果Session中的username對象為空白或不存在則跳轉到登陸頁,否則將它顯示出來                if (string.IsNullOrEmpty(username))                {                    context.Response.Redirect("Login3.ashx");                }                else                {                    context.Response.Write(username);                }            }        }        public bool IsReusable        {            get            {                return false;            }        }    }

    如下為填寫使用者名稱為admin點擊“登入”捕獲的資料,請求的是Login3.ashx,裡面的處理是將使用者名稱儲存寫入到了Session中,而可以看到響應標題中,有一個寫Cookie的過程,而且Cookie的內容為ASP.NET_SessionId。


    


    當Login3.ashx驗證使用者名稱正確後,立刻跳轉到TestLogin3.ashx,如為跳轉時捕獲的資料,可以看到在請求的標題中攜帶有Cookie,而Cookie的值正是上面寫Session時儲存到用戶端的ASP.NET_SessionId。


    


    TestLogin3中的處理是,驗證.Session["username"]是否存在,存在則取其值並顯示。而驗證是否存在和取其值的過程都是根據請求中的Cookie中的ASP.NET_SessionId來做的。

    上例即可說明Session機制的原理。下面通過手動用Cookie來類比Session來做相同的執行個體,以便大家理解的更深刻。

  手動用Cookie來類比Session機制

    添加四個檔案:Login2.html、Login2.ashx(做登入處理);TestLogin2.ashx(作為網站內部的其它頁面)、SessionMgr.cs(類比.net內部對Session對象的封裝)。代碼如下:

    Login2.html與上面的Login3.html代碼相同,只有title不同,代碼:   

<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>    <title>類比.net內建的Session機制</title></head><body>    <form action="../Login2.ashx" method="get">        使用者名稱:<input type="text" name="username" />        <br />        密 碼:<input type="text" name="password"/>        <br />        <input type="submit" name="login" value="提交" />    </form></body></html>

    SessionMgr.cs:    

  //自訂Session的操作類    public class SessionMgr    {        //Static在.net framework啟動並執行時候一直存在        //自訂一個Dictionary類型的對象,來類比.net內建的Session對象        private static Dictionary<Guid,string> testSession=new Dictionary<Guid,string>();        //寫(自訂)session        public static void WriteSession(Guid id, string value)        {            testSession[id] = value;        }        //判斷是否已經存在編號為id的(自訂)session        public static bool IsWriteSession(Guid id)        {            return testSession.Keys.Contains(id);        }        //擷取編號為id的(自訂)session的value值        public static string Get(Guid id)        {            return testSession[id];        }    }

    Login2.ashx:  

  public class Login2 : IHttpHandler    {        public void ProcessRequest(HttpContext context)        {            context.Response.ContentType = "text/html";            //取登陸時填寫的使用者名稱、密碼            string UserName=context.Request["username"];            string PassWord = context.Request["password"];            //登陸時,判斷使用者名稱是否正確(這裡設定為admin),正確則儲存使用者名稱到Session,並跳轉到TestLogin3.ashx            //否則清空輸入框,頁面不變            if (UserName == "admin")            {                //關鍵點                Guid id = Guid.NewGuid();//產生一個session編號,Guid可產生一個全球唯一的編碼,來類比.net內建Session的ASP.NET_SessionId                SessionMgr.WriteSession(id, UserName);//寫入(自訂的)session,將UserName與產生的id綁定(藉助於SessionMgr類)                HttpCookie cookie1 = new HttpCookie("sessionid", id.ToString());//只將id儲存到cookie中,並未儲存對應的資料(UserName)                context.Response.SetCookie(cookie1);                context.Response.Redirect("TestLogin2.ashx");            }            else            {                string html = CommonHelper.RenderHtml("Login2.html", null);                context.Response.Write(html);            }        }        public bool IsReusable        {            get            {                return false;            }        }    }

    TestLogin2.ashx:  

    public class TestLogin2 : IHttpHandler    {        public void ProcessRequest(HttpContext context)        {            context.Response.ContentType = "text/html";            HttpCookie cookie = context.Request.Cookies["sessionid"];            //請求此頁時,如果cookie為null則跳轉到登陸頁            //如果不為空白,則取出cookie的值作為一個Guid編號            if (cookie == null)            {                context.Response.Redirect("Login2.ashx");            }            else            {                Guid id = new Guid(cookie.Value);                //判斷伺服器端的Dictionary中是否包含編號為id的對象                //如果包含則取出其value,並顯示                //否則跳轉到登陸頁                if (SessionMgr.IsWriteSession(id))                {                    string value = SessionMgr.Get(id);                    context.Response.Write(value);                }                else                {                    context.Response.Write("<script>alert('沒有登入!');</script>");                    context.Response.Redirect("Login2.ashx");                }            }        }        public bool IsReusable        {            get            {                return false;            }        }    }

    上例中用一個Dictionary<id,value>對象來類比Session對象,用它的id(Guid)來類比.NET自動產生的編號(ASP.NET_SessionId),用它的value來類比要寫入Session的資料。


瀏覽器禁用Cookie問題

  上面介紹Cookie時談到,瀏覽器可以設定清除、禁用Cookie。而剛剛談到的Session機制是依賴於Cookie的。那麼按上面的意思,當禁用Cookie時就意味著ASP.NET_SessionId 無法回傳伺服器,Session也就無法使用。是否是這樣?答案肯定是否,應對這種情況,經常用兩種方法:

  URL地址修正:

    即將該使用者Session的id資訊重寫到URL地址中。伺服器能夠解析重寫後的URL擷取Session的id。這樣即使用戶端不支援Cookie,也可以使用Session來記錄使用者狀態。

  表單隱藏欄位

    伺服器會自動修改表單,添加一個隱藏欄位,以便在表單提交時能夠把session id傳遞迴伺服器。例如:

    

總結

  之前寫過相關的文章,完全是理論的學習,現在所有的東西都可以通過寫代碼和瀏覽器調試來驗證自己的想法。上面是目前階段自己的理解,希望能協助大家理解ASP.NET的Session機制。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.