Asp.Net MVC 中的 Cookie(译)

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。


如有不对,请多多指教


参考:

Asp.Net MVC 中的 Cookie(译)的更多相关文章

  1. ASP.NET MVC中的两个Action之间值的传递--TempData

    一. ASP.NET MVC中的TempData 在ASP.NET MVC框架的ControllerBase中存在一个叫做TempData的Property,它的类型为TempDataDictiona ...

  2. Asp.net MVC中提交集合对象,实现Model绑定

    Asp.net MVC中的Model自动绑定功能,方便了我们对于request中的数据的处理, 从客户端的请求数据,自动地以Action方法参数的形式呈现.有时候我们的Action方法中想要接收数组类 ...

  3. (转)Asp.Net MVC中身份认证和授权

    MVC自带的ActionFilter 在Asp.Net WebForm的中要做到身份认证微软为我们提供了三种方式,其中最常用的就是我们的Form认证,需要配置相应的信息.例如下面的配置信息: < ...

  4. ASP.NET CORE中使用Cookie身份认证

    大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cook ...

  5. asp.net mvc 中[Authorize]在IE9以上版本关于FormsAuthentication.SetAuthCookie无效的问题 解决方案

    简单的解决方法是,在网站根目录,新增一个浏览器定义文件(browser definition file) 叫“App_Browsers”文件夹,然后里面放一个“IE10.browser”文件即可,网站 ...

  6. [Asp.net mvc]Asp.net mvc 中使用LocalStorage

    目前使用比较多的本地存储方案有比如Flash SharedObject.Google Gears.Cookie.LocalStorage.User Data.Open Database等方案.综合比较 ...

  7. ASP.NET MVC中controller和view相互传值的方式

    ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...

  8. [转]ASP.NET MVC中的两个Action之间值的传递--TempData

    本文转自:ASP.NET MVC中的两个Action之间值的传递--TempData 一. ASP.NET MVC中的TempData 在ASP.NET MVC框架的ControllerBase中存在 ...

  9. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

随机推荐

  1. Asp.net MVC4高级编程学习笔记-视图学习第三课Razor页面布局20171010

    Razor页面布局 1)  在布局模板页中使用@RenderBody标记来渲染主要内容.比如很多web页面说头部和尾部相同,中间内容部分使用@RenderBody来显示不同的页面内容. 2)  在布局 ...

  2. C-多个行内块布局

    1 消除间隔

  3. ubuntu 11.04侧边栏怎么添加图标

    打开想添加的软件,图标会出现在侧边栏,右击之,点Keep In Launcher即可

  4. PE格式第四讲,数据目录表之导入表,以及IAT表

    PE格式第四讲,数据目录表之导入表,以及IAT表 一丶IAT(地址表) 首先我们思考一个问题,程序加载的时候会调用API,比如我们以前写的标准PE 那么他到底是怎么去调用的? 他会Call 下边的Jm ...

  5. 跨平台的 NodeJS 组件解决 .NetCore 不支持 System.Drawing图形功能的若干问题

    问题 生成缩略图 生成验证码 生成二维码 给图片加水印 外部引用 Node  不解释  https://nodejs.org/en/download/ sharp 高性能缩略图  https://gi ...

  6. Java面向对象 其他对象

     Java面向对象  其他对象 知识概要:             (1)可变参数 (2)静态导入 (3)System (4)Runtime (5)Date  Calendar (6)Math 本 ...

  7. Java命令模式以及来自lambda的优化

    前言    设计模式是软件工程中一些问题的统一解决方案的模型,它的出现是为了解决一些普遍存在的,却不能被语言特性直接解决的问题,随着软件工程的发展,设计模式也会不断的进行更新,本文介绍的是经典设计模式 ...

  8. win10 uwp 通知列表

    经常看到小伙伴问,问已经绑定列表,在进行修改时,不会通知界面添加或删除.这时问题就在,一般使用的列表不会在添加时通知界面,因为他们没有通知. 本文:知道什么是通知的列表,如何去写一个通知列表 在 C# ...

  9. ELK系列~log4-nxlog-Fluentd-elasticsearch写json数据需要注意的几点

    经验与实践 前两篇文章里我们介绍了nxlog的日志收集和转发<ELK系列~Nxlog日志收集加转发(解决log4日志换行导致json转换失败问题)>,今天我们主要总结一下,在与log4和f ...

  10. 如何透彻分析Java开发人员

    第一部分:对于参加工作一年以内的同学.恭喜你,这个时候,你已经拥有了一份Java的工作. 这个阶段是你成长极快的阶段,而且你可能会经常加班.但是加班不代表你就可以松懈了,永远记得我说的那句话,从你入行 ...