本文章为asp.net Cookie操作 (msdn中摘录)的后续摘录
读取 Cookie
浏览器向服务器发出请求时,会随请求一起发送该服务器的 Cookie。在 ASP.NET 应用程序中,可以使用
HttpRequest
对象读取 Cookie,该对象可用作 Page 类的
Request
属性使用。HttpRequest 对象的结构与 HttpResponse 对象的结构基本相同,因此,可以从 HttpRequest 对象中读取 Cookie,并且读取方式与将 Cookie 写入 HttpResponse 对象的方式基本相同。下面的代码示例演示两种方法,通过这两种方法可获取名为 username
的 Cookie 的值,并将其值显示在
Label
控件中:
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 的值之前,应确保该 Cookie 存在;如果该 Cookie 不存在,将会收到
NullReferenceException
异常。还请注意在页面中显示 Cookie 的内容前,先调用
HtmlEncode
方法对 Cookie 的内容进行编码。这样可以确保恶意用户没有向 Cookie 中添加可执行脚本。有关 Cookie 安全性的更多信息,请参见“Cookie 和安全性”一节。
读取 Cookie 中子键值的方法与设置该值的方法类似。下面的代码示例演示获取子键值的一种方法:
if(Request.Cookies["userInfo"] != null)
{
Label1.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]);
Label2.Text =
Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]);
}
在上面的示例中,代码读取子键 lastVisit
的值,该值先前被设置为字符串表示形式的 DateTime 值。Cookie 将值存储为字符串,因此,如果要将 lastVisit
值作为日期使用,必须将其转换为适当的类型,如此示例所示:
DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);
Cookie 中的子键被类型化为
NameValueCollection
类型的集合。因此,获取单个子键的另一种方法是获取子键集合,然后再按名称提取子键值,如下面的示例所示:
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 的期日期
浏览器负责管理 Cookie,而 Cookie 的到期时间和日期可帮助浏览器管理 Cookie 的存储。因此,虽然可以读取 Cookie 的名称和值,但无法读取 Cookie 的到期日期和时间。当浏览器向服务器发送 Cookie 信息时,并不包括有效期信息。(Cookie 的
Expires
属性始终返回值为 0 的日期时间值。)如果您担心 Cookie 的到期日期,必须重新设置该 Cookie,该过程在“修改和删除 Cookie”一节中介绍。
读取 Cookie 集合
有时,您可能需要读取可供页面使用的所有 Cookie。若要读取可供页面使用的所有 Cookie 的名称和值,可以使用如下代码依次通过
Cookies
集合。
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();
上面的示例有一个限制:如果 Cookie 有子键,则会以一个名称/值字符串来显示子键。可以读取 Cookie 的
HasKeys
属性,以确定 Cookie 是否有子键。如果有,则可以读取子键集合以获取各个子键名称和值。可以通过索引值直接从 Values 集合中读取子键值。相应的子键名称可在
Values
集合的
AllKeys
成员中获得,该成员将返回一个字符串数组。还可以使用 Values 集合的
Keys
成员。但是,首次访问
AllKeys
属性时,该属性会被缓存。相比之下,每次访问 Keys 属性时,该属性都生成一个数组。因此在同一页请求的上下文内,在随后访问时,AllKeys 属性要快得多。
下面的示例演示对前一示例的修改。该示例使用
HasKeys
属性来测试是否存在子键,如果检测到子键,便从 Values 集合获取子键:
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 对象提取,如下面的示例所示:
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 的值之前,应确保该 Cookie 存在;如果该 Cookie 不存在,将会收到
NullReferen
ceException
异常。还
请注意在页面中显示 Cookie的内容前,先调用
HtmlEncode
方法对 Cookie 的内容进行编码。这样可以确保恶意用户没有向
Cookie 中添加可执行脚本。
读取 Cookie 中子键值的方法与设置该值的方法类似。下面的代码示例演示获取子键值的一种方法:
if(Request.Cookies["userInfo"] != null) { Label1.Text = Server.HtmlEncode(Request.Cookies["userInfo"]["userName"]); Label2.Text = Server.HtmlEncode(Request.Cookies["userInfo"]["lastVisit"]); }
在上面的示例中,代码读取子键 lastVisit
的值,该值先前被设置为字符串表示形式的 DateTime 值。Cookie 将值存储为字符串,
因此,如果要将 lastVisit
值作为日期使用,必须将其转换为适当的类型,如此示例所示:
DateTime dt;
dt = DateTime.Parse(Request.Cookies["userInfo"]["lastVisit"]);
Cookie 中的子键被类型化为
NameValueCollection
类型的集合。因此,获取单个子键的另一种方法是获取子键集合,然后再按名称提取子键值,如下面的示例所示:
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 的到期日期
浏览器负责管理 Cookie,而 Cookie 的到期时间和日期可帮助浏览器管理 Cookie 的存储。因此,虽然可以读取 Cookie 的名称和值,但无法读取 Cookie 的到期日期和时间。当浏览器向服务器发送 Cookie 信息时,并不包括有效期信息。(Cookie 的
Expires
属性始终返回值为 0 的日期时间值。)如果您担心 Cookie 的到期日期,必须重新设置该 Cookie,该过程在“修改和删除 Cookie”一节中介绍。
读取 Cookie 集合
有时,您可能需要读取可供页面使用的所有 Cookie。若要读取可供页面使用的所有 Cookie 的名称和值,可以使用如下代码依次通过
Cookies
集合。
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();
上面的示例有一个限制:如果 Cookie 有子键,则会以一个名称/值字符串来显示子键。可以读取 Cookie 的
HasKeys
属性,以确定 Cookie 是否有子键。如果有,则可以读取子键集合以获取各个子键名称和值。可以通过索引值直接从 Values 集合中读取子键值。相应的子键名称可在
Values
集合的
AllKeys
成员中获得,该成员将返回一个字符串数组。还可以使用 Values 集合的
Keys
成员。但是,首次访问
AllKeys
属性时,该属性会被缓存。相比之下,每次访问 Keys 属性时,该属性都生成一个数组。因此在同一页请求的上下文内,在随后访问时,AllKeys 属性要快得多。
下面的示例演示对前一示例的修改。该示例使用
HasKeys
属性来测试是否存在子键,如果检测到子键,便从 Values 集合获取子键:
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 对象提取,如下面的示例所示:
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 的过程涉及创建一个具有新值的新 Cookie,然后将其发送到浏览器来覆盖客户端上的旧版本 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 的到期日期时,浏览器便会丢弃这个现已过期的 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 的 Values 集合,该集合用于保存子键。首先通过从 Cookies 对象中获取 Cookie
来重新创建 Cookie。然后您就可以调用 Values 集合的
Remove
方法,将要删除的子键的名称传递给 Remove 方法。
接着,将 Cookie 添加到 Cookies 集合,这样 Cookie 便会以修改后的格式发送回浏览器。下面的代码示例演示如何删除
子键。在此示例中,要移除的子键的名称在变量中指定。
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 的安全性问题与从客户端获取数据的安全性问题类似。在应用程序中,Cookie 是另一种形式的用户输入,因此很容易被他们非法获取和利用。由于 Cookie 保存在用户自己的计算机上,因此,用户至少能看到您存储在 Cookie 中的数据。用户还可以在浏览器向您发送 Cookie 之前更改该 Cookie。
千万不要在 Cookie 中存储敏感信息,如用户名、密码、信用卡号等等。不要在 Cookie 中放置任何不应由用户掌握的内容,也不要放可能被其他窃取 Cookie 的人控制的内容。
同样,不要轻信从 Cookie 中得到的信息。不要假定数据与您写出时相同;处理 Cookie 值时采用的安全措施应该与处理网页中用户键入的数据时采用的安全措施相同。本主题前面的示例演示在页面中显示值前,先对 Cookie 内容进行 HTML 编码的方法,这与在显示从用户处得到的任何信息之前的做法相同。
Cookie 以明文形式在浏览器和服务器间发送,任何可以截获 Web 通信的人都可以读取 Cookie。可以设置 Cookie 属性,使 Cookie 只能在使用安全套接字层 (SSL) 的连接上传输。SSL 并不能防止保存在用户计算机上的 Cookie 被读取或操作,但可防止 Cookie 在传输过程中被读取,因为 Cookie 已被加密。有关更多信息,请参见 Web 应用程序的基本安全实施策略。
确定浏览器是否接受 Cookie
用户可将其浏览器设置为拒绝接受 Cookie。在不能写入 Cookie 时不会引发任何错误。同样,浏览器也不向服务器发送有关其当前 Cookie 设置的任何信息。
确定 Cookie 是否被接受的一种方法是尝试编写一个 Cookie,然后再尝试读取该 Cookie。如果无法读取您编写的 Cookie,则可以假定浏览器不接受 Cookie。
下面的代码示例演示如何测试浏览器是否接受 Cookie。此示例由两个页面组成。第一个页面写出 Cookie,然后将浏览器重定向到第二个页面。第二个页面尝试读取该 Cookie。然后再将浏览器重定向回第一个页面,并将带有测试结果的查询字符串变量添加到 URL。
第一个页面的代码如下所示:
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"]);
}
}
}
该页面首先测试以确定是不是回发,如果不是,则查找包含测试结果的查询字符串变量名 AcceptsCookies
。如果不存在查询字符串变量,表示测试还未完成,因此代码会写出一个名为 TestCookie
的 Cookie。写出 Cookie 后,该示例调用
Redirect
来切换到 TestForCookies.aspx 测试页。附加到测试页 URL 的信息是一个名为 redirect
的查询字符串变量,该变量包含当前页的 URL;这样就能在执行测试后重定向回此页面。
测试页可完全由代码组成;不需要包含控件。下面的代码示例阐释了该测试页。
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 存在,则立即删除。测试完成后,代码通过 redirect
查询字符串变量传递给它的 URL 构造一个新的 URL。新 URL 也包括一个包含测试结果的查询字符串变量。最后一步是使用新 URL 将浏览器重定向到最初页面。
该示例的一个改进是可将 Cookie 测试结果保存到永久存储区(如数据库)中,这样就不必在用户每次查看最初页面时都重复进行测试。(默认情况下,在会话状态中存储测试结果需要 Cookie。)
Cookie 和会话状态
当用户导航到您的站点时,服务器为该用户建立唯一的会话,会话将一直延续到用户访问结束。ASP.NET 会为每个会话维护会话状态信息,应用程序可在会话状态信息中存储用户特定信息。有关更多信息,请参见会话状态概述主题。
ASP.NET 必须跟踪每个用户的会话 ID,以便可以将用户映射到服务器上的会话状态信息。默认情况下,ASP.NET 使用非永久性 Cookie 来存储会话状态。但是,如果用户已在浏览器上禁用 Cookie,会话状态信息便无法存储在 Cookie 中。
ASP.NET 提供了无 Cookie 会话作为替代。可以将应用程序配置为不将会话 ID 存储在 Cookie 中,而存储在站点中页面的 URL 中。如果应用程序依赖于会话状态,可以考虑将其配置为使用无 Cookie 会话。但是在较少的情况下,如果用户与他人共享 URL(可能是用户将 URL 发送给同事,而该用户的会话仍然处于活动状态),则最终这两个用户可能共享同一个会话,结果将难以预料。有关将应用程序配置为使用无 Cookie 会话的更多信息,请参见 ASP.NET 状态管理概述主题。