Asp.Net MVC 中的 Cookie(譯)

Asp.Net MVC 中的 Cookie(譯)

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

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

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

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

  • Cookie與特定的站點關聯

Cookie與一個Web站點相關聯,而不是特定的頁面,所以無論用戶請求你服務器的什麼頁面,瀏覽器和服務器都會交換cookie信息。 瀏覽器會爲每一個不一樣的Web站點分別存儲Cookie,保證每一個Cookie對應特定的Web站點。java

  • 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集合中。下面的例子展現了兩個編寫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。


若有不對,請多多指教


參考:

相關文章
相關標籤/搜索