有关cookies的一些小测试与总结

  起因

  口文艺果然很能忽悠。一天他突然跑来问我,知道IE7 session覆盖问题怎么解决吗?说了半天才明白他的意思,所谓的session覆盖,就是由选项卡带来的,一次登陆同一网站两次,使用不同账号,在一个浏览器的两个选项卡中,如此则后面登陆的session会覆盖前面登陆的session,“乱套了”。但我到觉得这没什么,回想做web那两年似乎没对这个很关注过。

  实际上,我接触的浏览器,除了IE6,基本上都会出现这种“乱套",这没什么,好的网站应该对此有出色的处理。只有IE6,在另启一个进程时,不会有这种覆盖。IE8和Firefox更是无论选项卡还是重新点开一个浏览器,都会共享session,共享cookies。

  文艺的忽悠没掀起风浪,不过我到是想复习一下以前做web时候的cookies应用,再稍微深入一点。

  一些小测试

  使用工具

  我使用了VS2008,浏览器是IE8,为了排除影响,还需要另外一个浏览器来浏览此网页,一个HTTP嗅探工具,我使用的是HttpWatch。

  C#中有关Cookies的写入


代码1
1 //单值形式
2  HttpCookie cookie1 = new HttpCookie("c1");
3 cookie1.Value = "val";
4
5  //集合形式
6  HttpCookie cookie2 = new HttpCookie("c2");
7 cookie2.Values.Add("thekey", "thevalue");
8
9 //写入到Response当中
10 Response.Cookies.Add(cookie1);
11 Response.Cookies.Add(cookie2);

  以上就是C#中最简单的两种Cookies写入,你可以在一个新建网站中添加一个页面(我的是SetCookies.aspx),在Page_Load中写入以上代码,以后不再赘述此类问题。

  使用嗅探工具查看HTTP报文,发现接收到的报文头中有:

  Set-Cookie    c1=val; path=/

  Set-Cookie    c2=thekey=thevalue; path=/

  说明Cookies已经进行设置了

  确保你的网站中有一个没有代码的空白页,我这里是自动创建的Default.aspx,在新选项卡中打开(注意不要关闭你的浏览器),你可以发现,你获取到了:

  Cookie    c1=val; c2=thekey=thevalue

  为什么我浏览一个与设置Cookies不相干的页面,依然能够得到Cookies呢?

  Cookies的路径(Path)

  当一个浏览器,访问一个域(Domain)时,实际上是会将能找到的所有符合条件的Cookies都进行筛选后,加入到报文当中,发送给服务器的。这种条件的筛选包括了域,路径,过期时间等。代码1中,在设置Cookies时并未对其作用范围进行设置,所以是整个网站下都可以访问到。

  上例收到报文中,path=/ 代表了根目录范围,即Cookies对此域下所有页面有效。

  在你项目中新添加一个子路径 son,新建一个页面(我的是SetCookies.aspx),复制代码1并对其进行稍微的改动:

代码2
1 HttpCookie cookie1 = new HttpCookie("son_c1");
2 cookie1.Value = "val";
3 cookie1.Path = "/CookieTest/son";
4  //CookieTest是我的虚拟路径名,你也可以写成
5  //cookie1.Path = Request.ApplicationPath + "/son";
6  
7 Response.Cookies.Add(cookie1);

  先关闭之前打开的测试用浏览器,这样是确保释放掉内存中的Cookies。先浏览根目录下的SetCookies页面,然后浏览son下你新建的,最后浏览根目录下的首页,从HttpWatch中看看你得到了什么?

  Set-Cookie    son_c1=val; path=/CookieTest/son

  这是son/SetCookies.aspx页面中设置的。可以发现,根目录并不能访问到子目录下设置的son_c1=val,但子目录可以访问到全局下的Cookies。Path的设置起作用了。

  关掉浏览器,尝试修改son/SetCookies.aspx如下行

cookie1.Path = "/cookieTest/son";

  即修改路径的大小写,反复浏览此页,会发现它始终在设置,但并没有读取自己设置的Cookies,把链接虚拟目录改为cookieTest,则可以读取。无奈,似乎path并不去兼容大小写,你只能在你的网站建设当中注意一些了。实际开发中,对这个问题有没有很好的解决方案呢?我要留意一下了。

  PS:实际上,大小写路径不同步的问题并不仅仅是在你设置某个子目录时候存在,只不过,从报文你就可以发现,系统每次都为Path 设置了"/"根目录,以解决URL大小写与cookies 不同步带来的无法访问问题。但如果你要具体细化到某一子目录下,这种曲线救国就无效了。

  你还可以试着把Path设置成某一个页面的路径,看看能否把你这个页面设置的cookies限定为只能单个页面访问?结果是有效的。

  过期时间

  和缓存一样,Cookies这种存储形式也有其一个有效时间的概念。你可以通过Expires(DateTime)来设置此Cookies的过期时间。

  

1 HttpCookie cookie1 = new HttpCookie("c1");
2 cookie1.Value = "val";
3
4 //设置过期时间为一天后
5 cookie1.Expires = DateTime.Now.AddDays(1);
6
7 Response.Cookies.Add(cookie1);

  打开C:\Documents and Settings\用户名\Cookies,你可以发现最新的txt文件中有一个名称类似用户名@local(根据你的测试项目而定)的文件,这就是你刚才设置的Cookies文件本地形式。仅在IE下才是此路径。我们之前的例子没有设置过期时间,默认为只存在于浏览器的内存形式中,只有当你设置了过期时间,才会有本地文件。这也是之前为什么我建议先将浏览器关闭再重新测试的原因。尝试将本地物理文件删除,

  在过期时间问题的判断上,其他浏览器我不清楚(不是做WEB的很不专业),至少IE和Firefox是有区别的。这里援引网上能搜到的一篇文章里的一段话:“在遇到web服务器返回的HTTP报文中有Set-Cookie字段时,Firefox判断cookie是否失效是看Set-Cookie中expires里的时间是否比该Web服务器时间新;而IE判断cookie是否失效是看Set-Cookie中expires里的时间是否比本地时间新。”

  如果你想查看Firefox的cookies,最好找一款查看工具。

  

  Cookies的域(Domain)

  先从一个小小的恶作剧开始:

  首先访问你的Cookies设置页面,确认本地有此Cookies的txt文件,修改其文件名为一个不相干的名字(例如caoz@kkk.txt),重新启动浏览器,查看Deafault页的报文,发现并没有Cookies发动给服务器,把此文件名改回原来,再次浏览Deafault页,没想到依旧没有Cookies发送给服务器。

  换一种方式,打扫干净痕迹后,我们修改这个Cookies文件的内容,把里面有localhost字样的修改为kkk,再次浏览Deafault页,你会发现,不但你没获取到信息,就连那个本地文件都消失了。

  我猜想这也许是IE的一种安全策略。

  每一次的浏览,浏览器会根据某种匹配方式去查找与URL中域可以关联上的Cookies文件,再发送前大概还要验证一次文件内记录的域是否合法。例如上例,大概是首先找到了 XXX@localhost.txt,再验证其中每条记录的过期时间,域,路径。

  HTTP协议中也为Cookies提供了域的设置,反映在C#中,即是HttpCookie的Domain属性.下面是一段代码,我们设置一个Cookies,他的域为"www.cooktest.com".


1 HttpCookie cookie1 = new HttpCookie("c1");
2 cookie1.Value = "val";
3  //设置Domain
4  cookie1.Domain = "www.cooktest.com";
5
6 Response.Cookies.Add(cookie1);

  继续用之前的方法来看HTTP报文。虽然每次浏览页面,都会有 "Set-Cookie    c1=val; domain=www.cooktest.com; path=/ ",但你会发现页面请求的报文中并没有这一项,实际上他并没有如你所愿的SET成功。这是因为你现在域,是localhost:XXXX形式的,援引一篇文章的话:设置cookie的服务器必须是此域名内的服务器。那就是说,一个域名为www.myserver.com的服务器不能够设置一个domain属性值为www.yourserver.com的cookie。这很明显是基于安全的考虑。

  为了测试,我们不得不使用修改本地hosts文件的方式,这也是做WEB开发常用的手段。进入C:\WINDOWS\system32\drivers\etc,记事本方式打开hosts文件。添加一行127.0.0.1    www.cooktest.com 并保存,使用新域名替换你刚才的localhost(别忘了带着端口号).OK,你得到了“Cookie    c1=val”,成功。对于域的设置,还可以更宽泛些。它支持主域和众多二级域之间的Cookies公用,通过设置Domain为".yoursite.com".

  打开你的hosts文件,继续添加"127.0.0.1    stock.cooktest.com","127.0.0.1    sport.cooktest.com","127.0.0.1    www.cooktest.cn",几项,并且保存。修改上例代码,将域设置为“.cooktest.com”,然后浏览测试,首先用主域设置Cookies。如所预料的,stock.cooktest.com和sport.cooktest.com都能正常接收到Cookies,而www.cooktest.cn则不行。

  我们继续折磨这个字符串“.cooktest.com”,不断对他进行瘦身:

  "cooktest.com"  看过有的文章“对这个参数(domain)有效值的唯一限制是他必须包含至少两个点号”。但前面这个测试确是可以通过的,你完全可以设置成这种形式。

  "cooktest"  这个事通不过的

  "cooktest."  依旧通不过

  现在我们大概可以看出来了,子域和主域的共享形式基本就限定在 yoursite.xxx 这中形式了。  

  设置同样key同样Domain的Cookies,不管所设置页面是处于主域或者子域下,都会有覆盖效果。 

  主域与子域的合并

  如果一个子域中,存在着一个和主域相同键但不同值的Cookies,当我访问子域时,浏览器会发送给服务器哪一个值呢?

  这次的测试我们需要两个设置页面了。在同一级目录下添加main.aspx和sub.aspx两个页面,在cs代码中设置Cookies,使用同样的key,但两者的value不同,并且让两者的Domain不相同。

  main.aspx

1 HttpCookie cookie1 = new HttpCookie("c");
2 cookie1.Value = "v1";
3 cookie1.Domain = "cooktest.com";
4 Response.Cookies.Add(cookie1);
sub.aspx

1 HttpCookie cookie1 = new HttpCookie("c");
2 cookie1.Value = "v2";
3 cookie1.Domain = "sport.cooktest.com";
4 Response.Cookies.Add(cookie1);
OK, 然后首先浏览 http://www.cooktest.com:端口号/CookieTest/main.aspx,再浏览 http://sport.cooktest.com:端口号/CookieTest/sub.aspx 以上两步模拟了一个设置域为主域,而另一个设置相同键,不同值,但域为子域的情况,现在让我们看看 http://sport.cooktest.com:端口号/CookieTest/ 得到了什么:

  Cookie    c=v1; c=v2

  IE合并了两个Cookies!同样的试验,我在Firefox上又试了一下,是针对网上看到的“IE会合并而Firefox则会子域覆盖主域”而做的,但结果并不是这样,所以我们基本可以认这种情况下,浏览器是会合并Cookies的。这可能给你获取HTTP请求的Cookies值时带来麻烦,所以即使是在不同域名下也尽量避免使用同样的键。

  Cookies跨域

  从上面的几个测试就可以看出,某一站点并不能设置其他不相干域的Cookies。如果我有www.cooktest.com,www.cooktest.cn等多个域名,怎么让其共享某些Cookies数据呢?SSO(单点登录)的问题即是如此。网上可以找到很多单点登录的解决方案,但我想无论如何也离不开访问多个站点来设置吧?在此不多研究了,希望以后有机会可以看一些源码。

  session与Cookies

  我们都知道,大多数默认情况下,浏览器和服务器维持一个会话使用的手段即是利用Cookies,创建一个sessionid,使得浏览器进程和服务器上维持的记录得以一一对应上。我们新建一个页面,在里面设置Session属性的值,再浏览本站下其他网页,可以看到类似:“Cookie    ASP.NET_SessionId=k103m5mg021bi4451msqgyv3”的信息,这既是一个sessionid.其路径为根目录,并且是HttpOnly的。

  javascript的Cookies操作

  有时候我们需要用JS脚本来设置一些Cookies:

var d = new Date();
d.setDate(d.getDate()
+ 1);
document.cookie
= "c=v100;expires=" + (d.toDateString()) +";path=/;domain=sport.cooktest.com";
你并不能在HTTP报文中看到这些设置,也许是在客户端执行了。但它确实可以获取得到。如上例所示,JS设置Cookies需要人工写成一个合法的字符串,各项之间用分号分隔,如果有必要,使用escape函数对value值进行编码。

   

  声明

  本文所做的测试参考了一些文章,并且对其进行了检验。目的单纯就是为了总结自己学所,供自己以后查看。结果不能保证一定正确,请看到文章的人不要一味相信,也自行测试独立思考才好。

  Cookies是个大话题,本文涉及内容很浅薄,希望以后有机会接触更深入的例如SSO,浏览器处理机制,session机制等深入的内容。

原文地址:https://www.cnblogs.com/apodemakeles/p/1866857.html