Asp.Net MVC 中的 Cookie(譯)


Asp.Net MVC 中的 Cookie(譯)

  • Cookie是請求服務器或訪問Web頁面時攜帶的一個小的文本信息。

Cookie為Web應用程序中提供了一種存儲特定用戶信息的方法。Cookie的值是字符串類型,且對用戶是可見的。

  • Cookie隨着每次RequestResponse在瀏覽器和服務器之間交換數據。

如果一個用戶請求服務器上的一個頁面,服務器除了返回請求的頁面,也返回了一個包含日期和時間的Cookie。這個Cookie存儲在用戶硬盤上的一個文件夾上。稍后,如果用戶再次訪問服務器,當用戶輸入URL時,瀏覽器會在本地硬盤上查看與該URL相關聯的Cookie。如果Cookie存在,瀏覽器會將Cookie隨着請求一起發送。然后,服務器可以讀取發送過來的Cookie信息,用戶上次訪問該站點的日期和時間。您可以使用這些信息向用戶顯示一條消息,或者檢查一個過期日期。

  • Cookie與特定的站點關聯

Cookie與一個Web站點相關聯,而不是特定的頁面,因此不管用戶請求你服務器的什么頁面,瀏覽器和服務器都會交換cookie信息。 瀏覽器會為每個不同的Web站點分別存儲Cookie,保證每個Cookie對應特定的Web站點。

  • Cookie保持會話狀態

Cookie可以幫助服務器存儲訪問者的信息。通俗的說,Cookie是保持Web應用程序連續性的一種方式,即會話狀態管理。因為Http請求是無狀態的,一些列請求中服務器並不知道請求來自哪些用戶,所以可以使用Cookie來唯一標識用戶,維護會話狀態。

Cookie用於許多目的,所有這些都與幫助網站記住用戶有關。例如,一個進行投票的站點可能會使用Cookie作為布爾值來指示用戶的瀏覽器是否已經參與投票,這樣用戶就不能進行兩次投票。一個要求用戶登錄的網站可能會使用Cookie來記錄用戶已經登錄的情況,這樣用戶就不必繼續輸入憑證了。


Cookie限制

大多數瀏覽器支持最多4096字節的Cookie。由於這個限制,cookie最好用於存儲少量數據,或者更好的是,像用戶ID這樣的標識符。

瀏覽器也會限制Web網站在用戶的電腦上可以存儲多少Cookie。大多數瀏覽器只允許每個站點提供20個Cookie。如果你想儲存更多,那么之前老的Cookie就會被丟棄。一些瀏覽器還會限制Cookie的總數量(不區分站點)。

盡管Cookie對Web應用程序非常有用,但是應用程序不應該依賴於Cookie。不要使用Cookie來支持關鍵敏感數據。


寫Cookie

瀏覽器負責管理用戶電腦上的Cookie。Cookie通過HttpResonse對象被發送到瀏覽器,該對象公開了一個名為Cookies的集合。想要發送到瀏覽器的任何Cookie都必須添加到這個集合中。在創建Cookie時,指定一個名稱和值。每個Cookie都必須有一個惟一的名稱,以便瀏覽器讀取時可以識別它。因為Cookie是按名稱存儲的,因此多個Cookie名稱相同時,新的Cookie值會覆蓋掉之前的。

可以設置Cookie的日期和過期時間。當用戶訪問寫Cookie的站點時,瀏覽器會刪除過期的Cookie。Cookie的有效期可以是50年。

如果沒有設置Cookie的過期時間,就創建了Cookie,那么它並不會存儲在用戶的硬盤上。這種Cookie是作為用戶會話信息的一部分來維護的。當用戶關閉瀏覽器時,cookie就會被丟棄。像這樣的非持久cookie對於需要在短時間內存儲的信息非常有用,或者出於安全原因,不應該將其寫入到客戶機計算機上的磁盤上。例如,如果用戶正在使用公共計算機,而不希望將cookie寫入磁盤,則非持久性cookie非常有用。

您可以通過多種方式將Cookie添加到Cookie集合中。下面的例子展示了兩個編寫Cookie的方法:

//第一種
Response.Cookies["userName"].Value = "patrick";
Response.Cookies["userName"].Expires = DateTime.Now.AddDays(1);
//第二種
HttpCookie aCookie = new HttpCookie("lastVisit");
aCookie.Value = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

該示例將兩個Cookie添加到Cookie集合中,其中一個名為用戶名,另一個為上次訪問時間。對於第一種方法,Cookie集合的值可以直接讀寫。因為Cookie繼承自NameObjectCollectionBase集合類型,所以可以直接獲取Cookie。

對於第二種方法,創建一個HttpCookie對象的實例,設置它的屬性,然后通過Add方法將其添加到cookie集合中。Cookie的名稱通過構造函數添加。

這兩個示例都完成了相同的任務,將Cookie寫入瀏覽器。在這兩種方法中,過期值必須為DateTime類型。因為所有的Cookie值都以字符串形式存儲,所以日期時間值會轉換為字符串。

多個值的Cookie

Cookie可以存儲一個值,比如用戶名或最后一次訪問。也可以在一個Cookie中存儲多個鍵值對。鍵值對被稱為子鍵。(子鍵的布局非常類似於URL中的查詢字符串。)例如,您可以創建一個名為userInfo的cookie,它具有子健userName和lastVisit,而不是創建兩個單獨的Cookie。

子健可以將相關的或類似的信息放入一個Cookie中。如果所有信息都在一個Cookie中,Cookie屬性如過期將應用於所有信息。(相反,如果您想為不同類型的信息分配不同的過期日期,則應該將信息存儲在單獨的cookie中。)

帶有子鍵的Cookie也可以幫助限制Cookie文件的大小。之前提到,Cookie通常被限制為4096字節,並且每個站點能存儲20多個Cookie。通過使用一個帶有子鍵的Cookie,可以降低站點Cookie數量的限制。此外,一個Cookie自身就占用了大約50個字符(過期信息,等等),加上您存儲在其中的值的長度,所有這些值都指向4096字節的限制。如果您存儲5個子鍵而不是5個單獨的cookie,您可以節省單個Cookie的開銷,並且還可以節省大約200個字節。

創建有子鍵的Cookie:

//第一種
Response.Cookies["userInfo"]["userName"] = "patrick";
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);
//第二種
HttpCookie aCookie = new HttpCookie("userInfo");
aCookie.Values["userName"] = "patrick";
aCookie.Values["lastVisit"] = DateTime.Now.ToString();
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

Cookie作用域

默認情況下,一個站點的有過期時間的所有Cookie都存儲在客戶硬盤上,請求該網站時所有的Cookie都被發送到服務器。換句話說,網站上的每一個頁面都能獲得該站點的所有Cookie。但是,您可以通過兩種方式來限制Cookie的范圍:

  • 將Cookie的范圍限制在服務器上的一個文件夾中,這允許您將Cookie限制在站點上的一個應用程序中。

  • 將范圍設置為域,允許您指定域中的哪些子域可以訪問cookie。

要將cookie限制在服務器上的一個文件夾中,可以設置cookie的路徑屬性,如下面的例子:

HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + DateTime.Now.ToString();
appCookie.Expires = DateTime.Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);

路徑既可以是站點根下的物理路徑,也可以是虛擬路徑。這樣做的效果是,Cookie只可用於Application1文件夾或虛擬根目錄中的頁面。例如,如果你的網站是www.contoso.com,在前面的示例中創建的Cookie可以與路徑http://www.contoso.com/Application1/頁面,該文件夾下任何頁面。然而,Cookie不會在其他應用程序中可用的頁面,比如http://www.contoso.com/Application2/或者http://www.contoso.com/

Cookie的限制和作用域

默認情況下,Cookie與特定的域相關聯。
例如,如果您的站點是www.contoso.com,那么當用戶請求該站點的任何頁面時,您所寫的Cookie將被發送到服務器。
(這可能不包括帶有特定路徑值的cookie。)
如果你的站點有子域名,例如,contoso.com,sales.contoso.com,以及support.contoso.com,那么你就可以把Cookies與特定的子域名聯系起來。

代碼示例:

Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "support.contoso.com";

當域以這種方式設置時,Cookie將只在指定子域中的頁面可用。您還可以使用域屬性來創建一個可以在多個子域中共享的Cookie,如下面的示例所示:

Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "contoso.com";

此時Cookie既可以主域,也可以用於sales.contoso.com和support.contoso.com等子域。


讀取Cookie

當瀏覽器向服務器發出請求時,Cookie會隨着請求一起發送到服務器上。在你的應用程序,您可以使用HttpRequest來讀取cookie。

代碼示例:

if(Request.Cookies["userName"] != null)
{
    HttpCookie aCookie = Request.Cookies["userName"];
    var cookiText = Server.HtmlEncode(aCookie.Value);
}

在嘗試獲取Cookie的值之前,應該確保Cookie存在;如果Cookie不存在,將得到NullReferenceException異常。還要注意,HtmlEncode方法被調用來對Cookie的內容進行編碼。這確保了惡意用戶沒有將可執行腳本添加到Cookie中。

在Cookie中讀取子鍵的值也類似於設置它。下面的代碼示例展示了獲取子鍵值的一種方法

if(Request.Cookies["userInfo"] != null)
{
    var name = 
        Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);

    var visit =
        Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}

讀取鍵值對lastVisit的值,該值之前被設置為DateTime類型。但是Cookie將值存儲為字符串,因此如果您想使用lastVisit的值作為日期,則必須將其轉換為適當的類型。

DateTime dt= DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);

Cookie中的子鍵是NameValueCollection類型。因此,獲取子鍵的另一種方法是獲取子鍵集合,然后按名稱提取子鍵值,如下面的示例所示:

if(Request.Cookies["userInfo"] != null)
{
    System.Collections.Specialized.NameValueCollection  UserInfoCookieCollection;
    UserInfoCookieCollection = Request.Cookies["userInfo"].Values;
   var name = 
        Server.HtmlEncode(UserInfoCookieCollection["userName"]);
    var visit =
        Server.HtmlEncode(UserInfoCookieCollection["lastVisit"]);
}

Cookie的過期時間

瀏覽器負責管理Cookie,Cookie的過期時間和日期幫助瀏覽器管理其存儲的Cookie。因此,雖然您可以讀取Cookie的名稱和值,但是您不能讀取cookie的過期日期和時間。當瀏覽器向服務器發送Cookie信息時,瀏覽器不包含過期信息。(Cookie的過期屬性總是返回一個日期時間值為0。)如果您擔心cookie的過期日期,您必須重新設置它。

讀取Cookie集合

獲取所有的Cookie

代碼示例;

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Cookie name = " + Server.HtmlEncode(aCookie.Name) 
        + "<br />");
    output.Append("Cookie value = " + Server.HtmlEncode(aCookie.Value)
        + "<br /><br />");
}

上一個示例的有一個不足的地方,如果cookie有子鍵,會將子鍵作為單個name/value字符串顯示。您可以讀取cookie的haskey屬性,以確定cookie是否有子鍵。如果有,您可以讀取子鍵集合以獲得單獨的子鍵名和值。
您也可以通過索引值直接從值集合中讀取子鍵值。相應的子鍵名可以在值集合的AllKeys成員中找到,它返回一個字符串數組。您還可以使用值集合的鍵值。但是,AllKeys屬性在第一次被訪問時被緩存。相反,鍵屬性在每次訪問時都構建一個數組。由於這個原因,在相同頁面請求的上下文中,AllKeys屬性要快得多。

下面的例子顯示了前面的示例的修改。它使用HasKeys屬性來測試子鍵,如果檢測到子鍵,那么這個示例將從值集合中獲得子鍵:

for(int i=0; i<Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if(aCookie.HasKeys)
    {
        for(int j=0; j<aCookie.Values.Count; j++)
        {
            subkeyName = Server.HtmlEncode(aCookie.Values.AllKeys[j]);
            subkeyValue = Server.HtmlEncode(aCookie.Values[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue + 
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}

或者,您可以將子鍵提取為NameValueCollection對象,如下面的例子所示:

System.Text.StringBuilder output = new System.Text.StringBuilder();
HttpCookie aCookie;
string subkeyName;
string subkeyValue;

for (int i = 0; i < Request.Cookies.Count; i++)
{
    aCookie = Request.Cookies[i];
    output.Append("Name = " + aCookie.Name + "<br />");
    if (aCookie.HasKeys)
    {
        System.Collections.Specialized.NameValueCollection CookieValues = 
            aCookie.Values;
        string[] CookieValueNames = CookieValues.AllKeys;
        for (int j = 0; j < CookieValues.Count; j++)
        {
            subkeyName = Server.HtmlEncode(CookieValueNames[j]);
            subkeyValue = Server.HtmlEncode(CookieValues[j]);
            output.Append("Subkey name = " + subkeyName + "<br />");
            output.Append("Subkey value = " + subkeyValue + 
                "<br /><br />");
        }
    }
    else
    {
        output.Append("Value = " + Server.HtmlEncode(aCookie.Value) +
            "<br /><br />");
    }
}

Cookie的刪除和修改

修改Cookie

不能直接在獲取到Cookie后就修改Cookie的值,因為Cookie存儲在用戶的硬盤上(Request.Cookies["Key"].Value="some",在這里是不起作用的),必須在Response.Cookies["Key"].Value="SomeNew"中修改才可以。其實就是服務器中設置的Cookie新值,覆蓋了用戶瀏覽地上的Cookie的舊值。

代碼示例:

int counter;
if (Request.Cookies["counter"] == null)
    counter = 0;
else
{
    counter = int.Parse(Request.Cookies["counter"].Value);
}
counter++;

Response.Cookies["counter"].Value = counter.ToString();
Response.Cookies["counter"].Expires = DateTime.Now.AddDays(1);

刪除Cookie

將Cookie從用戶的硬磁盤上刪除,和修改Cookie原理是一樣的。不能直接刪除Cookie,因為Cookie在用戶的硬盤上。但是,可以創建一個新Cookie,其名稱與要刪除的Cookie相同,但要將Cookie的過期時間設置為比今天更早的日期。當瀏覽器檢查Cookie的過期時間,瀏覽器將丟棄已經過時的Cookie。

代碼示例:

HttpCookie aCookie;
string cookieName;
int limit = Request.Cookies.Count;
for (int i=0; i<limit; i++)
{
    cookieName = Request.Cookies[i].Name;
    aCookie = new HttpCookie(cookieName);
    aCookie.Expires = DateTime.Now.AddDays(-1);
    Response.Cookies.Add(aCookie);
}

子鍵的修改和刪除

修改一個單獨的子鍵與創建它是一樣的

如:

Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);

要刪除一個單獨的子鍵,您可以操作Cookie的值集合,該集合包含子鍵。
首先從Cookie集合中獲取一個指定的Cookie,然后調用該cookie值集合的remove方法,參數即為要刪除的子鍵名稱,再把該Cookie添加到集合中。

如下代碼示例:

string subkeyName;
subkeyName = "userName";
HttpCookie aCookie = Request.Cookies["userInfo"];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);

Cookies安全性

Cookie的安全性問題類似於從客戶端獲取數據。對於應用程序來說,可以把Cookie看作是另一種形式的用戶輸入。因為Cookie是保存在用戶自己的硬盤上,所以Cookie對用戶來說是可見的,也是可以修改的。

因為Cookie對用戶是可見的所以不能在Cookie中存儲敏感數據,比如用戶名、密碼、信用卡號等等。

因為Cookie也是可修改的,所以對程序來說從Cookie獲取的信息,也需要驗證和判斷。不能簡單的認為Cookie中的數據就和我們預期的數據一樣。

Cookie是在瀏覽器和服務器之間是作為純文本發送的,任何能夠攔截Web流量的人都可以攔截Cookie。但是可以設置Cookie只有在使用安全套接字層(SSL)的情況下才會傳輸Cookie,這樣在傳輸時會對Cookie加密。但是在用戶的硬盤上,Cookie就不受SSL保護了。


確定瀏覽器是否接受cookie

用戶可以設置瀏覽器拒絕Cookie。如果不能寫入Cookie,也不會拋出錯誤。同樣,瀏覽器也不會向服務器發送關於當前Cookie設置的任何信息。

客戶端驗證

最簡單的是使用JS來判斷

  if (navigator.cookieEnabled) {
        alert("Cookie 可用");
    }
    else {
        alert("Cookie 禁用");
    }

服務端驗證

cookie屬性並不表示是否啟用了cookie。它只表明當前瀏覽器本身是否支持cookie。

在服務端判斷Cookie是否被接受的一種方法是嘗試寫一個Cookie,然后再試着讀取該Cookie。如果不能讀取所編寫的Cookie,那么就可以假定瀏覽器關閉了Cookie。


如有不對,請多多指教


參考:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM