|
Cookie 提供了一種在 Web 應(yīng)用程序中存儲(chǔ)用戶特定信息的方法。例如,當(dāng)用戶訪問(wèn)您的站點(diǎn)時(shí),您可以使用 Cookie 存儲(chǔ)用戶首選項(xiàng)或其他信息。當(dāng)該用戶再次訪問(wèn)您的網(wǎng)站時(shí),應(yīng)用程序便可以檢索以前存儲(chǔ)的信息。
什么是 Cookie?
Cookie 是一小段文本信息,伴隨著用戶請(qǐng)求和頁(yè)面在 Web 服務(wù)器和瀏覽器之間傳遞。Cookie 包含每次用戶訪問(wèn)站點(diǎn)時(shí) Web 應(yīng)用程序都可以讀取的信息。
例如,如果在用戶請(qǐng)求站點(diǎn)中的頁(yè)面時(shí)應(yīng)用程序發(fā)送給該用戶的不僅僅是一個(gè)頁(yè)面,還有一個(gè)包含日期和時(shí)間的 Cookie,用戶的瀏覽器在獲得頁(yè)面的同時(shí)還獲得了該 Cookie,并將它存儲(chǔ)在用戶硬盤(pán)上的某個(gè)文件夾中。
以后,如果該用戶再次請(qǐng)求您站點(diǎn)中的頁(yè)面,當(dāng)該用戶輸入 URL 時(shí),瀏覽器便會(huì)在本地硬盤(pán)上查找與該 URL 關(guān)聯(lián)的 Cookie。如果該 Cookie 存在,瀏覽器便將該 Cookie 與頁(yè)請(qǐng)求一起發(fā)送到您的站點(diǎn)。然后,應(yīng)用程序便可以確定該用戶上次訪問(wèn)站點(diǎn)的日期和時(shí)間。您可以使用這些信息向用戶顯示一條消息,也可以檢查到期日期。
Cookie 與網(wǎng)站關(guān)聯(lián),而不是與特定的頁(yè)面關(guān)聯(lián)。因此,無(wú)論用戶請(qǐng)求站點(diǎn)中的哪一個(gè)頁(yè)面,瀏覽器和服務(wù)器都將交換 Cookie 信息。用戶訪問(wèn)不同站點(diǎn)時(shí),各個(gè)站點(diǎn)都可能會(huì)向用戶的瀏覽器發(fā)送一個(gè) Cookie;瀏覽器會(huì)分別存儲(chǔ)所有 Cookie。
Cookie 幫助網(wǎng)站存儲(chǔ)有關(guān)訪問(wèn)者的信息。一般來(lái)說(shuō),Cookie 是一種保持 Web 應(yīng)用程序連續(xù)性(即執(zhí)行狀態(tài)管理)的方法。除短暫的實(shí)際交換信息的時(shí)間外,瀏覽器和 Web 服務(wù)器間都是斷開(kāi)連接的。對(duì)于用戶向 Web 服務(wù)器發(fā)出的每個(gè)請(qǐng)求,Web 服務(wù)器都會(huì)單獨(dú)處理。但是在很多情況下,Web 服務(wù)器在用戶請(qǐng)求頁(yè)時(shí)識(shí)別出用戶會(huì)十分有用。例如,購(gòu)物站點(diǎn)上的 Web 服務(wù)器跟蹤每位購(gòu)物者,這樣站點(diǎn)就可以管理購(gòu)物車(chē)和其他的用戶特定信息。因此,Cookie 可以作為一種名片,提供相關(guān)的標(biāo)識(shí)信息幫助應(yīng)用程序確定如何繼續(xù)執(zhí)行。
使用 Cookie 能夠達(dá)到多種目的,所有這些目的都是為了幫助網(wǎng)站記住用戶。例如,一個(gè)實(shí)施民意測(cè)驗(yàn)的站點(diǎn)可以簡(jiǎn)單地將 Cookie 作為一個(gè) Boolean 值,用它來(lái)指示用戶的瀏覽器是否已參與了投票,這樣用戶便無(wú)法進(jìn)行第二次投票。要求用戶登錄的站點(diǎn)則可以通過(guò) Cookie 來(lái)記錄用戶已經(jīng)登錄,這樣用戶就不必每次都輸入憑據(jù)。
Cookie 的限制
大多數(shù)瀏覽器支持最大為 4096 字節(jié)的 Cookie。由于這限制了 Cookie 的大小,最好用 Cookie 來(lái)存儲(chǔ)少量數(shù)據(jù),或者存儲(chǔ)用戶 ID 之類(lèi)的標(biāo)識(shí)符。用戶 ID 隨后便可用于標(biāo)識(shí)用戶,以及從數(shù)據(jù)庫(kù)或其他數(shù)據(jù)源中讀取用戶信息。(有關(guān)存儲(chǔ)用戶信息安全建議的信息,請(qǐng)參見(jiàn)下面的“Cookie 和安全性”一節(jié)。)
瀏覽器還限制站點(diǎn)可以在用戶計(jì)算機(jī)上存儲(chǔ)的 Cookie 的數(shù)量。大多數(shù)瀏覽器只允許每個(gè)站點(diǎn)存儲(chǔ) 20 個(gè) Cookie;如果試圖存儲(chǔ)更多 Cookie,則最舊的 Cookie 便會(huì)被丟棄。有些瀏覽器還會(huì)對(duì)它們將接受的來(lái)自所有站點(diǎn)的 Cookie 總數(shù)作出絕對(duì)限制,通常為 300 個(gè)。
您可能遇到的 Cookie 限制是用戶可以將其瀏覽器設(shè)置為拒絕接受 Cookie。如果定義一個(gè) P3P 隱私策略,并將其放置在網(wǎng)站的根目錄中,則更多的瀏覽器將接受您站點(diǎn)的 Cookie。但是,您可能會(huì)不得不完全放棄 Cookie,而通過(guò)其他機(jī)制來(lái)存儲(chǔ)用戶特定的信息。存儲(chǔ)用戶信息的常用方法是會(huì)話狀態(tài),但會(huì)話狀態(tài)依賴于 Cookie,這一點(diǎn)在后面的“Cookie 和會(huì)話狀態(tài)”一節(jié)中說(shuō)明。
注意
有關(guān) Web 應(yīng)用程序中的狀態(tài)管理和用于保存信息的選項(xiàng)的更多信息,請(qǐng)參見(jiàn) ASP.NET 狀態(tài)管理概述 和 ASP.NET 狀態(tài)管理建議。
雖然 Cookie 在應(yīng)用程序中非常有用,但應(yīng)用程序不應(yīng)依賴于能夠存儲(chǔ) Cookie。不要使用 Cookie 支持關(guān)鍵功能。如果應(yīng)用程序必須依賴于 Cookie,則可以通過(guò)測(cè)試確定瀏覽器是否將接受 Cookie。請(qǐng)參見(jiàn)本主題后面的“檢查瀏覽器是否接受 Cookie”一節(jié)。
編寫(xiě) Cookie
瀏覽器負(fù)責(zé)管理用戶系統(tǒng)上的 Cookie。Cookie 通過(guò) HttpResponse 對(duì)象發(fā)送到瀏覽器,該對(duì)象公開(kāi)稱為 Cookies 的集合??梢詫?HttpResponse 對(duì)象作為 Page 類(lèi)的 Response 屬性來(lái)訪問(wèn)。要發(fā)送給瀏覽器的所有 Cookie 都必須添加到此集合中。創(chuàng)建 Cookie 時(shí),需要指定 Name 和 Value。每個(gè) Cookie 必須有一個(gè)唯一的名稱,以便以后從瀏覽器讀取 Cookie 時(shí)可以識(shí)別它。由于 Cookie 按名稱存儲(chǔ),因此用相同的名稱命名兩個(gè) Cookie 會(huì)導(dǎo)致其中一個(gè) Cookie 被覆蓋。
還可以設(shè)置 Cookie 的到期日期和時(shí)間。用戶訪問(wèn)編寫(xiě) Cookie 的站點(diǎn)時(shí),瀏覽器將刪除過(guò)期的 Cookie。只要應(yīng)用程序認(rèn)為 Cookie 值有效,就應(yīng)將 Cookie 的有效期設(shè)置為這一段時(shí)間。對(duì)于永不過(guò)期的 Cookie,可將到期日期設(shè)置為從現(xiàn)在起 50 年。
注意
用戶可隨時(shí)清除其計(jì)算機(jī)上的 Cookie。即便存儲(chǔ)的 Cookie 距到期日期還有很長(zhǎng)時(shí)間,但用戶還是可以決定刪除所有 Cookie,清除 Cookie 中存儲(chǔ)的所有設(shè)置。
如果沒(méi)有設(shè)置 Cookie 的有效期,仍會(huì)創(chuàng)建 Cookie,但不會(huì)將其存儲(chǔ)在用戶的硬盤(pán)上。而會(huì)將 Cookie 作為用戶會(huì)話信息的一部分進(jìn)行維護(hù)。當(dāng)用戶關(guān)閉瀏覽器時(shí),Cookie 便會(huì)被丟棄。這種非永久性 Cookie 很適合用來(lái)保存只需短時(shí)間存儲(chǔ)的信息,或者保存由于安全原因不應(yīng)該寫(xiě)入客戶端計(jì)算機(jī)上的磁盤(pán)的信息。例如,如果用戶在使用一臺(tái)公用計(jì)算機(jī),而您不希望將 Cookie 寫(xiě)入該計(jì)算機(jī)的磁盤(pán)中,這時(shí)就可以使用非永久性 Cookie。
可以通過(guò)多種方法將 Cookie 添加到 Cookies 集合中。下面的示例演示兩種編寫(xiě) Cookie 的方法:
復(fù)制代碼 代碼如下:
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);
此示例向 Cookies 集合添加兩個(gè) Cookie,一個(gè)名為 userName,另一個(gè)名為 lastVisit。對(duì)于第一個(gè) Cookie,Cookies 集合的值是直接設(shè)置的。可以通過(guò)這種方式向集合添加值,因?yàn)?Cookies 是從 NameObjectCollectionBase 類(lèi)型的專(zhuān)用集合派生的。
對(duì)于第二個(gè) Cookie,代碼創(chuàng)建了一個(gè) HttpCookie 類(lèi)型的對(duì)象實(shí)例,設(shè)置其屬性,然后通過(guò) Add 方法將其添加到 Cookies 集合。在實(shí)例化 HttpCookie 對(duì)象時(shí),必須將該 Cookie 的名稱作為構(gòu)造函數(shù)的一部分進(jìn)行傳遞。
這兩個(gè)示例都完成了同一任務(wù),即向?yàn)g覽器寫(xiě)入一個(gè) Cookie。在這兩種方法中,有效期值必須為 DateTime 類(lèi)型。但是,lastVisited 值也是日期時(shí)間值。因?yàn)樗?Cookie 值都存儲(chǔ)為字符串,因此,必須將日期時(shí)間值轉(zhuǎn)換為 String。
多值 Cookie
可以在 Cookie 中存儲(chǔ)一個(gè)值,如用戶名和上次訪問(wèn)時(shí)間。也可以在一個(gè) Cookie 中存儲(chǔ)多個(gè)名稱/值對(duì)。名稱/值對(duì)稱為子鍵。(子鍵布局類(lèi)似于 URL 中的查詢字符串。)例如,不要?jiǎng)?chuàng)建兩個(gè)名為 userName 和 lastVisit 的單獨(dú) Cookie,而可以創(chuàng)建一個(gè)名為 userInfo 的 Cookie,其中包含兩個(gè)子鍵 userName 和 lastVisit。
您可能會(huì)出于多種原因來(lái)使用子鍵。首先,將相關(guān)或類(lèi)似的信息放在一個(gè) Cookie 中很方便。此外,由于所有信息都在一個(gè) Cookie 中,所以諸如有效期之類(lèi)的 Cookie 屬性就適用于所有信息。(反之,如果要為不同類(lèi)型的信息指定不同的到期日期,就應(yīng)該把信息存儲(chǔ)在單獨(dú)的 Cookie 中。)
帶有子鍵的 Cookie 還可幫助您限制 Cookie 文件的大小。正如前面“Cookie 的限制”一節(jié)中所提到的,Cookie 通常限制為 4096 字節(jié),并且每個(gè)站點(diǎn)最多可存儲(chǔ) 20 個(gè) Cookie。使用帶子鍵的單個(gè) Cookie,使用的 Cookie 數(shù)就不會(huì)超過(guò)分配給站點(diǎn)的 20 個(gè)的限制。此外,一個(gè) Cookie 會(huì)占用大約 50 個(gè)字符的系統(tǒng)開(kāi)銷(xiāo)(用于保存有效期信息等),再加上其中存儲(chǔ)的值的長(zhǎng)度,其總和接近 4096 字節(jié)的限制。如果存儲(chǔ)五個(gè)子鍵而不是五個(gè)單獨(dú)的 Cookie,便可節(jié)省單獨(dú) Cookie 的系統(tǒng)開(kāi)銷(xiāo),節(jié)省大約 200 字節(jié)。
若要?jiǎng)?chuàng)建帶子鍵的 Cookie,您可以使用編寫(xiě)單個(gè) Cookie 的各種語(yǔ)法。下面的示例演示用于編寫(xiě)同一 Cookie 的兩種方法,其中的每個(gè) Cookie 都帶有兩個(gè)子鍵:
復(fù)制代碼 代碼如下:
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 的范圍
默認(rèn)情況下,一個(gè)站點(diǎn)的全部 Cookie 都一起存儲(chǔ)在客戶端上,而且所有 Cookie 都會(huì)隨著對(duì)該站點(diǎn)發(fā)送的任何請(qǐng)求一起發(fā)送到服務(wù)器。也就是說(shuō),一個(gè)站點(diǎn)中的每個(gè)頁(yè)面都能獲得該站點(diǎn)的所有 Cookie。但是,可以通過(guò)兩種方式設(shè)置 Cookie 的范圍:
將 Cookie 的范圍限制到服務(wù)器上的某個(gè)文件夾,這允許您將 Cookie 限制到站點(diǎn)上的某個(gè)應(yīng)用程序。
將范圍設(shè)置為某個(gè)域,這允許您指定域中的哪些子域可以訪問(wèn) Cookie。
將 Cookie 限制到某個(gè)文件夾或應(yīng)用程序
若要將 Cookie 限制到服務(wù)器上的某個(gè)文件夾,請(qǐng)按下面的示例設(shè)置 Cookie 的 Path 屬性:
復(fù)制代碼 代碼如下:
HttpCookie appCookie = new HttpCookie("AppCookie");
appCookie.Value = "written " + DateTime.Now.ToString();
appCookie.Expires = DateTime.Now.AddDays(1);
appCookie.Path = "/Application1";
Response.Cookies.Add(appCookie);
注意
還可以通過(guò)將 Cookie 直接添加到 Cookies 集合的方式來(lái)編寫(xiě) Cookie,如先前的示例所示。
路徑可以是站點(diǎn)根目錄下的物理路徑,也可以是虛擬根目錄。所產(chǎn)生的效果是 Cookie 只能用于 Application1 文件夾或虛擬根目錄中的頁(yè)面。例如,如果您的站點(diǎn)名稱為 http://www.contoso.com/,則在前面示例中創(chuàng)建的 Cookie 將只能用于路徑為 http://www.contoso.com/Application1/ 的頁(yè)面以及該文件夾下的所有頁(yè)面。但是,Cookie 將不能用于其他應(yīng)用程序中的頁(yè)面,如 http://www.contoso.com/Application2/ 或 http://www.contoso.com/ 中的頁(yè)面。
注意
在某些瀏覽器中,路徑區(qū)分大小寫(xiě)。您無(wú)法控制用戶如何在其瀏覽器中鍵入 URL,但如果應(yīng)用程序依賴于與特定路徑相關(guān)的 Cookie,請(qǐng)確保您創(chuàng)建的所有超鏈接中的 URL 與 Path 屬性值的大小寫(xiě)相匹配。
限制 Cookie 的域范圍
默認(rèn)情況下,Cookie 與特定域關(guān)聯(lián)。例如,如果您的站點(diǎn)是 http://www.contoso.com/,那么當(dāng)用戶向該站點(diǎn)請(qǐng)求任何頁(yè)時(shí),您編寫(xiě)的 Cookie 就會(huì)被發(fā)送到服務(wù)器。(這可能不包括帶有特定路徑值的 Cookie。)如果站點(diǎn)具有子域(例如,contoso.com、sales.contoso.com 和 support.contoso.com),則可以將 Cookie 與特定的子域關(guān)聯(lián)。若要執(zhí)行此操作,請(qǐng)?jiān)O(shè)置 Cookie 的 Domain 屬性,如此示例所示:
Response.Cookies["domain"].Value = DateTime.Now.ToString();
Response.Cookies["domain"].Expires = DateTime.Now.AddDays(1);
Response.Cookies["domain"].Domain = "support.contoso.com";
當(dāng)以此方式設(shè)置域時(shí),Cookie 將僅可用于指定的子域中的頁(yè)面。還可以使用 Domain 屬性創(chuàng)建可在多個(gè)子域間共享的 Cookie,如下面的示例所示:
復(fù)制代碼 代碼如下:
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
瀏覽器向服務(wù)器發(fā)出請(qǐng)求時(shí),會(huì)隨請(qǐng)求一起發(fā)送該服務(wù)器的 Cookie。在 ASP.NET 應(yīng)用程序中,可以使用 HttpRequest 對(duì)象讀取 Cookie,該對(duì)象可用作 Page 類(lèi)的 Request 屬性使用。HttpRequest 對(duì)象的結(jié)構(gòu)與 HttpResponse 對(duì)象的結(jié)構(gòu)基本相同,因此,可以從 HttpRequest 對(duì)象中讀取 Cookie,并且讀取方式與將 Cookie 寫(xiě)入 HttpResponse 對(duì)象的方式基本相同。下面的代碼示例演示兩種方法,通過(guò)這兩種方法可獲取名為 username 的 Cookie 的值,并將其值顯示在 Label 控件中:
復(fù)制代碼 代碼如下:
if(Request.Cookies["userName"] != null)
Label1.Text = Server.HtmlEncode(Request.Cookies["userName"].Value);
if(Request.Cookies["userName"] != null)
{
HttpCookie aCookie = Request.Cookies["userName"];
Label1.Text = Server.HtmlEncode(aCookie.Value);
}
在嘗試獲取 Cookie 的值之前,應(yīng)確保該 Cookie 存在;如果該 Cookie 不存在,將會(huì)收到 NullReferenceException 異常。還請(qǐng)注意在頁(yè)面中顯示 Cookie 的內(nèi)容前,先調(diào)用 HtmlEncode 方法對(duì) Cookie 的內(nèi)容進(jìn)行編碼。這樣可以確保惡意用戶沒(méi)有向 Cookie 中添加可執(zhí)行腳本。有關(guān) Cookie 安全性的更多信息,請(qǐng)參見(jiàn)“Cookie 和安全性”一節(jié)。
注意
由于不同的瀏覽器存儲(chǔ) Cookie 的方式不同,因此,同一計(jì)算機(jī)上的不同瀏覽器沒(méi)有必要能夠讀取彼此的 Cookie。例如,如果使用 InterNET Explorer 測(cè)試一個(gè)頁(yè)面,然后再使用其他瀏覽器進(jìn)行測(cè)試,那么后者將不會(huì)找到 InterNET Explorer 保存的 Cookie。
讀取 Cookie 中子鍵值的方法與設(shè)置該值的方法類(lèi)似。下面的代碼示例演示獲取子鍵值的一種方法:
復(fù)制代碼 代碼如下:
if(Request.Cookies["userInfo"] != null)
{
Label1.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);
Label2.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}
在上面的示例中,代碼讀取子鍵 lastVisit 的值,該值先前被設(shè)置為字符串表示形式的 DateTime 值。Cookie 將值存儲(chǔ)為字符串,因此,如果要將 lastVisit 值作為日期使用,必須將其轉(zhuǎn)換為適當(dāng)?shù)念?lèi)型,如此示例所示:
DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);
Cookie 中的子鍵被類(lèi)型化為 NameValueCollection 類(lèi)型的集合。因此,獲取單個(gè)子鍵的另一種方法是獲取子鍵集合,然后再按名稱提取子鍵值,如下面的示例所示:
復(fù)制代碼 代碼如下:
if(Request.Cookies["userInfo"] != null)
{
System.Collections.Specialized.NameValueCollection
UserInfoCookieCollection;
UserInfoCookieCollection = Request.Cookies["userInfo"].Values;
Label1.Text =
Server.HtmlEncode(UserInfoCookieCollection["userName"]);
Label2.Text =
Server.HtmlEncode(UserInfoCookieCollection["lastVisit"]);
}
更改 Cookie 的到期日期
瀏覽器負(fù)責(zé)管理 Cookie,而 Cookie 的到期時(shí)間和日期可幫助瀏覽器管理 Cookie 的存儲(chǔ)。因此,雖然可以讀取 Cookie 的名稱和值,但無(wú)法讀取 Cookie 的到期日期和時(shí)間。當(dāng)瀏覽器向服務(wù)器發(fā)送 Cookie 信息時(shí),并不包括有效期信息。(Cookie 的 Expires 屬性始終返回值為 0 的日期時(shí)間值。)如果您擔(dān)心 Cookie 的到期日期,必須重新設(shè)置該 Cookie,該過(guò)程在“修改和刪除 Cookie”一節(jié)中介紹。
注意
可以在向?yàn)g覽器發(fā)送 Cookie 之前讀取已在 HttpResponse 對(duì)象中設(shè)置的 Cookie 的 Expires 屬性。但是,您無(wú)法從返回的 HttpRequest 對(duì)象中獲取有效期。
讀取 Cookie 集合
有時(shí),您可能需要讀取可供頁(yè)面使用的所有 Cookie。若要讀取可供頁(yè)面使用的所有 Cookie 的名稱和值,可以使用如下代碼依次通過(guò) Cookies 集合。
復(fù)制代碼 代碼如下:
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 />");
}
Label1.Text = output.ToString();
注意
在運(yùn)行此代碼時(shí),可能會(huì)看到一個(gè)名為 ASP.NET_SessionId 的 Cookie。ASP.NET 使用該 Cookie 來(lái)存儲(chǔ)您的會(huì)話的唯一標(biāo)識(shí)符。會(huì)話 Cookie 不會(huì)保存在您的硬盤(pán)上。有關(guān)會(huì)話 Cookie 的更多信息,請(qǐng)參見(jiàn)本主題后面的“Cookie 和會(huì)話狀態(tài)”一節(jié)。
上面的示例有一個(gè)限制:如果 Cookie 有子鍵,則會(huì)以一個(gè)名稱/值字符串來(lái)顯示子鍵??梢宰x取 Cookie 的 HasKeys 屬性,以確定 Cookie 是否有子鍵。如果有,則可以讀取子鍵集合以獲取各個(gè)子鍵名稱和值??梢酝ㄟ^(guò)索引值直接從 Values 集合中讀取子鍵值。相應(yīng)的子鍵名稱可在 Values 集合的 AllKeys 成員中獲得,該成員將返回一個(gè)字符串?dāng)?shù)組。還可以使用 Values 集合的 Keys 成員。但是,首次訪問(wèn) AllKeys 屬性時(shí),該屬性會(huì)被緩存。相比之下,每次訪問(wèn) Keys 屬性時(shí),該屬性都生成一個(gè)數(shù)組。因此在同一頁(yè)請(qǐng)求的上下文內(nèi),在隨后訪問(wèn)時(shí),AllKeys 屬性要快得多。
下面的示例演示對(duì)前一示例的修改。該示例使用 HasKeys 屬性來(lái)測(cè)試是否存在子鍵,如果檢測(cè)到子鍵,便從 Values 集合獲取子鍵:
復(fù)制代碼 代碼如下:
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 />");
}
}
Label1.Text = output.ToString();
或者,可將子鍵作為 NameValueCollection 對(duì)象提取,如下面的示例所示:
復(fù)制代碼 代碼如下:
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 />");
}
}
Label1.Text = output.ToString();
修改和刪除 Cookie
不能直接修改 Cookie。更改 Cookie 的過(guò)程涉及創(chuàng)建一個(gè)具有新值的新 Cookie,然后將其發(fā)送到瀏覽器來(lái)覆蓋客戶端上的舊版本 Cookie。下面的代碼示例演示如何更改存儲(chǔ)用戶對(duì)站點(diǎn)的訪問(wèn)次數(shù)的 Cookie 的值:
復(fù)制代碼 代碼如下:
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(即從用戶的硬盤(pán)中物理移除 Cookie)是修改 Cookie 的一種形式。由于 Cookie 在用戶的計(jì)算機(jī)中,因此無(wú)法將其直接移除。但是,可以讓瀏覽器來(lái)為您刪除 Cookie。該技術(shù)是創(chuàng)建一個(gè)與要?jiǎng)h除的 Cookie 同名的新 Cookie,并將該 Cookie 的到期日期設(shè)置為早于當(dāng)前日期的某個(gè)日期。當(dāng)瀏覽器檢查 Cookie 的到期日期時(shí),瀏覽器便會(huì)丟棄這個(gè)現(xiàn)已過(guò)期的 Cookie。下面的代碼示例演示刪除應(yīng)用程序中所有可用 Cookie 的一種方法:
復(fù)制代碼 代碼如下:
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);
}
修改或刪除子鍵修改單個(gè)子鍵的方法與創(chuàng)建它的方法相同,如下面的示例所示:
Response.Cookies["userInfo"]["lastVisit"] = DateTime.Now.ToString();
Response.Cookies["userInfo"].Expires = DateTime.Now.AddDays(1);
若要?jiǎng)h除單個(gè)子鍵,可以操作 Cookie 的 Values 集合,該集合用于保存子鍵。首先通過(guò)從 Cookies 對(duì)象中獲取 Cookie 來(lái)重新創(chuàng)建 Cookie。然后您就可以調(diào)用 Values 集合的 Remove 方法,將要?jiǎng)h除的子鍵的名稱傳遞給 Remove 方法。接著,將 Cookie 添加到 Cookies 集合,這樣 Cookie 便會(huì)以修改后的格式發(fā)送回瀏覽器。下面的代碼示例演示如何刪除子鍵。在此示例中,要移除的子鍵的名稱在變量中指定。
復(fù)制代碼 代碼如下:
string subkeyName;
subkeyName = "userName";
HttpCookie aCookie = Request.Cookies["userInfo"];
aCookie.Values.Remove(subkeyName);
aCookie.Expires = DateTime.Now.AddDays(1);
Response.Cookies.Add(aCookie);
Cookie 和安全性
Cookie 的安全性問(wèn)題與從客戶端獲取數(shù)據(jù)的安全性問(wèn)題類(lèi)似。在應(yīng)用程序中,Cookie 是另一種形式的用戶輸入,因此很容易被他們非法獲取和利用。由于 Cookie 保存在用戶自己的計(jì)算機(jī)上,因此,用戶至少能看到您存儲(chǔ)在 Cookie 中的數(shù)據(jù)。用戶還可以在瀏覽器向您發(fā)送 Cookie 之前更改該 Cookie。
千萬(wàn)不要在 Cookie 中存儲(chǔ)敏感信息,如用戶名、密碼、信用卡號(hào)等等。不要在 Cookie 中放置任何不應(yīng)由用戶掌握的內(nèi)容,也不要放可能被其他竊取 Cookie 的人控制的內(nèi)容。
同樣,不要輕信從 Cookie 中得到的信息。不要假定數(shù)據(jù)與您寫(xiě)出時(shí)相同;處理 Cookie 值時(shí)采用的安全措施應(yīng)該與處理網(wǎng)頁(yè)中用戶鍵入的數(shù)據(jù)時(shí)采用的安全措施相同。本主題前面的示例演示在頁(yè)面中顯示值前,先對(duì) Cookie 內(nèi)容進(jìn)行 HTML 編碼的方法,這與在顯示從用戶處得到的任何信息之前的做法相同。
Cookie 以明文形式在瀏覽器和服務(wù)器間發(fā)送,任何可以截獲 Web 通信的人都可以讀取 Cookie??梢栽O(shè)置 Cookie 屬性,使 Cookie 只能在使用安全套接字層 (SSL) 的連接上傳輸。SSL 并不能防止保存在用戶計(jì)算機(jī)上的 Cookie 被讀取或操作,但可防止 Cookie 在傳輸過(guò)程中被讀取,因?yàn)?Cookie 已被加密。有關(guān)更多信息,請(qǐng)參見(jiàn) Web 應(yīng)用程序的基本安全實(shí)施策略。
確定瀏覽器是否接受 Cookie
用戶可將其瀏覽器設(shè)置為拒絕接受 Cookie。在不能寫(xiě)入 Cookie 時(shí)不會(huì)引發(fā)任何錯(cuò)誤。同樣,瀏覽器也不向服務(wù)器發(fā)送有關(guān)其當(dāng)前 Cookie 設(shè)置的任何信息。
注意
Cookies 屬性不指示 Cookie 是否啟用。它僅指示當(dāng)前瀏覽器是否原本支持 Cookie。
確定 Cookie 是否被接受的一種方法是嘗試編寫(xiě)一個(gè) Cookie,然后再嘗試讀取該 Cookie。如果無(wú)法讀取您編寫(xiě)的 Cookie,則可以假定瀏覽器不接受 Cookie。
下面的代碼示例演示如何測(cè)試瀏覽器是否接受 Cookie。此示例由兩個(gè)頁(yè)面組成。第一個(gè)頁(yè)面寫(xiě)出 Cookie,然后將瀏覽器重定向到第二個(gè)頁(yè)面。第二個(gè)頁(yè)面嘗試讀取該 Cookie。然后再將瀏覽器重定向回第一個(gè)頁(yè)面,并將帶有測(cè)試結(jié)果的查詢字符串變量添加到 URL。
第一個(gè)頁(yè)面的代碼如下所示:
復(fù)制代碼 代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (Request.QueryString["AcceptsCookies"] == null)
{
Response.Cookies["TestCookie"].Value = "ok";
Response.Cookies["TestCookie"].Expires =
DateTime.Now.AddMinutes(1);
Response.Redirect("TestForCookies.ASPx?redirect=" +
Server.UrlEncode(Request.Url.ToString()));
}
else
{
Label1.Text = "Accept cookies = " +
Server.UrlEncode(
Request.QueryString["AcceptsCookies"]);
}
}
}
該頁(yè)面首先測(cè)試以確定是不是回發(fā),如果不是,則查找包含測(cè)試結(jié)果的查詢字符串變量名 AcceptsCookies。如果不存在查詢字符串變量,表示測(cè)試還未完成,因此代碼會(huì)寫(xiě)出一個(gè)名為 TestCookie 的 Cookie。寫(xiě)出 Cookie 后,該示例調(diào)用 Redirect 來(lái)切換到 TestForCookies.ASPx 測(cè)試頁(yè)。附加到測(cè)試頁(yè) URL 的信息是一個(gè)名為 redirect 的查詢字符串變量,該變量包含當(dāng)前頁(yè)的 URL;這樣就能在執(zhí)行測(cè)試后重定向回此頁(yè)面。
測(cè)試頁(yè)可完全由代碼組成;不需要包含控件。下面的代碼示例闡釋了該測(cè)試頁(yè)。
復(fù)制代碼 代碼如下:
protected void Page_Load(object sender, EventArgs e)
{
string redirect = Request.QueryString["redirect"];
string acceptsCookies;
if(Request.Cookies["TestCookie"] ==null)
acceptsCookies = "no";
else
{
acceptsCookies = "yes";
// Delete test cookie.
Response.Cookies["TestCookie"].Expires =
DateTime.Now.AddDays(-1);
}
Response.Redirect(redirect + "?AcceptsCookies=" + acceptsCookies,
true);
}
讀取重定向查詢字符串變量后,代碼嘗試讀取 Cookie。出于管理目的,如果該 Cookie 存在,則立即刪除。測(cè)試完成后,代碼通過(guò) redirect 查詢字符串變量傳遞給它的 URL 構(gòu)造一個(gè)新的 URL。新 URL 也包括一個(gè)包含測(cè)試結(jié)果的查詢字符串變量。最后一步是使用新 URL 將瀏覽器重定向到最初頁(yè)面。
該示例的一個(gè)改進(jìn)是可將 Cookie 測(cè)試結(jié)果保存到永久存儲(chǔ)區(qū)(如數(shù)據(jù)庫(kù))中,這樣就不必在用戶每次查看最初頁(yè)面時(shí)都重復(fù)進(jìn)行測(cè)試。(默認(rèn)情況下,在會(huì)話狀態(tài)中存儲(chǔ)測(cè)試結(jié)果需要 Cookie。)
Cookie 和會(huì)話狀態(tài)
當(dāng)用戶導(dǎo)航到您的站點(diǎn)時(shí),服務(wù)器為該用戶建立唯一的會(huì)話,會(huì)話將一直延續(xù)到用戶訪問(wèn)結(jié)束。ASP.NET 會(huì)為每個(gè)會(huì)話維護(hù)會(huì)話狀態(tài)信息,應(yīng)用程序可在會(huì)話狀態(tài)信息中存儲(chǔ)用戶特定信息。有關(guān)更多信息,請(qǐng)參見(jiàn)會(huì)話狀態(tài)概述主題。
ASP.NET 必須跟蹤每個(gè)用戶的會(huì)話 ID,以便可以將用戶映射到服務(wù)器上的會(huì)話狀態(tài)信息。默認(rèn)情況下,ASP.NET 使用非永久性 Cookie 來(lái)存儲(chǔ)會(huì)話狀態(tài)。但是,如果用戶已在瀏覽器上禁用 Cookie,會(huì)話狀態(tài)信息便無(wú)法存儲(chǔ)在 Cookie 中。
ASP.NET 提供了無(wú) Cookie 會(huì)話作為替代。可以將應(yīng)用程序配置為不將會(huì)話 ID 存儲(chǔ)在 Cookie 中,而存儲(chǔ)在站點(diǎn)中頁(yè)面的 URL 中。如果應(yīng)用程序依賴于會(huì)話狀態(tài),可以考慮將其配置為使用無(wú) Cookie 會(huì)話。但是在較少的情況下,如果用戶與他人共享 URL(可能是用戶將 URL 發(fā)送給同事,而該用戶的會(huì)話仍然處于活動(dòng)狀態(tài)),則最終這兩個(gè)用戶可能共享同一個(gè)會(huì)話,結(jié)果將難以預(yù)料。有關(guān)將應(yīng)用程序配置為使用無(wú) Cookie 會(huì)話的更多信息,請(qǐng)參見(jiàn) ASP.NET 狀態(tài)管理概述主題。
AspNet技術(shù):ASP.NET Cookie 操作實(shí)現(xiàn),轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。