一前言 Cookies想必所有人都了解 但是未必所有人都精通本文讲解了Cookies的各方面知识 并且提出来了最佳实践这是笔者在日常工作中的积累和沉澱 二基础知识 什么是Cookies Cookie 是一小段文本信息伴随着用户请求和页面在 Web 服务器和浏览器之间传递Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息 例如如果在用户请求站点中的页面时应用程序发送给该用户的不仅仅是一个页面还有一个包含日期和时间的 Cookie用户的浏览器在获得页面的同时还获得了该 Cookie并将它存储在用户硬盘上的某个文件夹中 以后如果该用户再次请求您站点中的页面当该用户输入 URL 时浏览器便会在本地硬盘上查找与该 URL 关联的 Cookie如果该 Cookie 存在浏览器便将该 Cookie 与页请求一起发送到您的站点然后应用程序便可以确定该用户上次访问站点的日期和时间您可以使用这些信息向用户显示一条消息也可以检查到期日期 Cookie 与网站关联而不是与特定的页面关联因此无论用户请求站点中的哪一个页面浏览器和服务器都将交换 Cookie 信息用户访问不同站点时各个站点都可能会向用户的浏览器发送一个 Cookie浏览器会分别存储所有 Cookie Cookie 帮助网站存储有关访问者的信息一般来说Cookie 是一种保持 Web 应用程序连续性(即执行状态管理)的方法除短暂的实际交换信息的时间外浏览器和 Web 服务器间都是断开连接的对于用户向 Web 服务器发出的每个请求Web 服务器都会单独处理但是在很多情况下Web 服务器在用户请求页时识别出用户会十分有用例如购物站点上的 Web 服务器跟蹤每位购物者这样站点就可以管理购物车和其他的用户特定信息因此Cookie 可以作为一种名片提供相关的标识信息帮助应用程序确定如何继续执行 使用 Cookie 能够达到多种目的所有这些目的都是为了帮助网站记住用户例如一个实施民意测验的站点可以简单地将 Cookie 作为一个 Boolean 值用它来指示用户的浏览器是否已参与了投票这样用户便无法进行第二次投票要求用户登录的站点则可以通过 Cookie 来记录用户已经登录这样用户就不必每次都输入凭据 Cookies如何存储 Cookies保存在用户的本地机器上不同的浏览器存储在不同的文件夹中并且按照域名分别保存即网站之间的Cookies不会彼此覆盖 IE浏览器的用户可以通过在本地的文档中找到Cookies的txt文件 不同操作系统的位置不同windows server /xp都保存在 C:\Documents and Settings\Administrator\Cookies 文件夹下 其中名称txt按照域名保存比如localhost域下的cookies为 administrator@localhost[]txt 或者 administrator@localhost[]txt 其中后面的[]和[]是随着每次保存交替变化的 Cookies如何传递 Cookies的信息是在Web服务器和浏览器之间传递的保存在Http请求中 ()请求页面 在请求一个页面的Http头中会将属于此页面的本地Cookies信息加在Http头中注意下面加粗的部分 GET /Cookies/Testaspx HTTP/ Host: localhost: UserAgent: Mozilla/ (Windows; U; Windows NT ; zhCN; rv:) Gecko/ Firefox/ GTB (NET CLR ) Accept: text/htmlapplication/xhtml+xmlapplication/xml;q=*/*;q= AcceptLanguage: zhcnzh;q= AcceptEncoding: gzipdeflate AcceptCharset: GButf;q=*;q= KeepAlive: Connection: keepalive Cookie: MyCommonTestCookieInfo=Pkid=&TestValue=aaabbbcccdddeee ()页面响应 如果页面要求写入Cookies信息则返回的Http如下注意加粗的部分 HTTP/x OK Server: ASPNET Development Server/ Date: Thu Aug :: GMT XAspNetVersion: SetCookie: MyCommonTestCookieInfo=Pkid=&TestValue=aaabbbcccdddeee; expires=Fri Aug :: GMT; path=/ CacheControl: private ContentType: text/html; charset=utf ContentLength: Connection: Close Cookies如何查看 ()查看Cookies的txt文件 IE用户可以直接查看Cookies的txt文件 比如C:\Documents and Settings\Administrator\Cookies\administrator@localhost[]txt ()使用插件 FF下使用Web Developer插件可以很方便的查看删除和修改Cookies 查看页面Cookies 三 Cookies高级知识 Cookie 的限制 大多数浏览器支持最大为 字节的 Cookie 浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量大多数浏览器只允许每个站点存储 个 Cookie注意这里的个是指主键值也就是条Cookies记录但是每个Cookies记录还可以包含若干子键下面会详细解释如果试图存储更多 Cookie则最旧的 Cookie 便会被丢弃有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制通常为 个 Cookies的存储格式 Cookies可以包含一个主键 主键再包含子键比如中获取Cookies的格式是 RequestCookies[key][subkey]ToString(); 其中的key就是主键subkey就是主键关联的子键 ()本地磁盘存储格式 MyCommonTestCookieInfoPkid=&TestValue=aaabbbcccdddeeelocalhost/* 其中的Pkid=&TestValue=aaabbbcccdddeee 是Cookies的值由于使用了subkey=subvalue的格式 所以此Cookies是包含子键的 ()Javascript中的Cookie格式 在Javascript中给的Cookie是一个字符串通过okies获取字符格式如下 MyCommonSubKey=Pkid=&TestValue=aaabbbcccdddeee; SingleKey=SingleKeyValue 上面的字符串包含了两个Cookies一个是不包含子键的SingleKey 一个是包含pkid和TextValue两个子键的MyCommonSubKey两个Cookie通过;分割 ()AspNet中的Cookies格式 和所有的服务器端语言一样AspNet中使用集合类保存Cookies集合 public sealed class HttpCookieCollection : NameObjectCollectionBase{} 通过HttpResquest和HttpResponse对象的Cookies属性可以获取和写入当前页面的Cookies Cookies的内容编码格式 Cookies的值中可以保存除了;以外的标点符号但是不能保存汉字保存汉字会出现乱码 所以对于Cookies中的内容要进行统一的编码和解码为了在浏览器端和服务器端都能够进行解码和编码 所以要统一使用UTF编码格式 主要是因为javascript中只能使用UTF编码格式 Cookies的Path属性 Cookies的Path属性表示当前的Cookies可以作用在网站的那个路径下 比如下面的两个同名的Cookies 允许存在两个同名但是Path不同的Cookies 无论是服务器端还是客户端在获取时优先获取本页路径下面的Cookies 也就是说如果在/chapter/路径下面的页面 获取testKey这个Cookies的值则只能获取到testValue这个值 Cookies的过期时间 如果保存Cookies时未设置过期时间 则Cookies的过期时间为当前浏览器进程有效即和Session一样关闭浏览器后则消失在中还可以通过设置HttpCookie对象的过期时间为DateTimeMinValue来指定此Cookies为跟随浏览器生效(这句话来之不易啊在脑袋等人的帮助下才查到的) 如果设置了过期时间并且大于当前时间则会保存Cookies值 如果设置了过期时间但是小于等于当前时间则清除Cookies值 Cookies与Session 有时我们会忽略Cookies与Session的关系但是两者是密不可分的 Session的唯一标示SessionID是通常保存在Cookies中的(也可以保存在URL中)对于AspNet而言SessionID保存在键值为ASPNET_SessionId的Cookies中如图 因为Cookies的存储数量是有限制的所以我们的系统在保存Cookies的时候一定要注意防止沖掉这一个关键的Cookies在下文介绍的最佳实践以强对象方式保存Cookies的代码中特意对这个Cookies做了处理 注意在客户端使用javascript脚本无法获取ASPNET_SessionId的Cookies 因为此Cookies在服务器端设置了HttpOnly属性为true ASPNet中HttpCookie对象的HttpOnly 属性 指定一个Cookie 是否可通过客户端脚本访问不能通过客户端脚本访问为 true否则为 false默认值为 false此属性并不能完全阻止客户端在本地获取cookies但是可以增加通过脚本直接获取的难度 Microsoft Internet Explorer 版本 Service Pack 和更高版本支持 Cookie 属性 HttpOnly Cookies加密 在设置Cookies的属性时有一个选项Secure用来控制Cookie的加密特性 如果通过 SSL 连接 (HTTPS) 传输 Cookie则为 true否则为 false默认为 false 如果我们保存一个Cookies并设置加密那么在非HTTPS的页面中无论是使用javascript还是服务器端都无法获得此Cookies但是在本地依然可以看到此Cookies的存在 Cookies与Ajax 如果Ajax请求访问一个服务器页面此服务器页面是可以向用户浏览器写入Cookies和Session的 四 Cookies最佳实践 在了解了Cookies的相关知识后下面提出最佳的事件方法其中包括客户端和服务器端两部分 ()AspNet 中保存Cookies 通常我们使用Request和Response对象来直接操作Cookies 写入Cookies ResponseCookies[k]Value = kValue;ResponseCookies[k][k] = kValue;ResponseCookiesAdd(new HttpCookie(k kValue)); 读取Cookies Request[k] ;RequestCookies[k]Value ;RequestCookies[k][k];RequestCookiesGet()Value; 注意Request[k]这个大家熟悉的获取get和post参数的方法同时还能够获取Cookies的值! 另外上面语句中的有些是必须通过Value属性访问的有些则不需要 ()以对象方式保存Cookies 下面提供一个可以以对象方式整体保存Cookies的工具类并且只占用一条Cookies所有的属性都存在子键上 源代码 /// /// Cookies基类将需要保存Cookies的数据类此类派生可以将强类型对象在Cookies中的保存和读取 /// /// /// ziqiuzhang created /// /// /// 假设MyCookiesInfo是从 从Cookies中获取对象 /// /// CookieInfo item = new CookieInfo(); //new以后已经从Cookies中构造了对象 /// /// 将对象保存在Cookies中 /// /// CookieInfo item = new CookieInfo(); /// itemvalue = test value; /// itemSetCookies(); //Cookies有效期为天 /// /// [SystemSerializable] public class CookieInfo { #region ==================== Constructed Method ==================== /// /// 构造函数 /// public CookieInfo() { } #endregion #region ==================== Public Method ==================== /// /// 得到当前Cookies的过期时间 /// /// 过期时间 public DateTime GetExpiresTime() { string cookieName = GetType()ToString(); if (HttpContextCurrentRequestCookies[cookieName] != null) { return HttpContextCurrentRequestCookies[cookieName]Expires; } return DateTimeMinValue; } /// /// 保存Cookies过期时间为浏览器关闭则失效 /// /// Cookies过期事件 /// 是否保存成功 public bool Save() { return thisSave(DateTimeMinValue); } /// /// 保存Cookies需要指定过期时间 /// /// Cookies过期事件 /// 是否保存成功 public bool Save(DateTime expiresTime) { string CookieName = GetType()ToString(); HttpCookie SessionCookie = null; //对 SessionId 进行备份 if (HttpContextCurrentRequestCookies[ASPNET_SessionId] != null) { string SesssionId = HttpContextCurrentRequestCookies[ASPNET_SessionId]ValueToString(); SessionCookie = new HttpCookie(ASPNET_SessionId); SessionCookieValue = SesssionId; } //设定cookie 过期时间 DateTime dtExpiry = expiresTime; HttpContextCurrentResponseCookies[CookieName]Expires = dtExpiry; //设定cookie 域名 string domain = stringEmpty; if (HttpContextCurrentRequestParams[HTTP_HOST] != null) { //domain = ; domain = HttpContextCurrentRequestParams[HTTP_HOST]ToString(); } //如果是或多级域名需要转化为 if (domainIndexOf() > ) { string[] temp = domainSplit(); if (tempLength >= ) { domain = temp[tempLength ]Trim() + + temp[tempLength ]Trim(); } HttpContextCurrentResponseCookies[CookieName]Domain = domain; } //把类的属性 写入Cookie PropertyInfo[] Propertys = GetType()GetProperties(); foreach (PropertyInfo pi in Propertys) { object oj = piGetValue(this null); Type type = piPropertyType; string valueStr = stringEmpty; if (oj != null && ojToString() != stringEmpty) { if (type == TypeGetType(SystemDateTime)) { valueStr = ((DateTime)oj)ToString(yyyy/MM/dd HH:mm:ss SystemGlobalizationDateTimeFormatInfoInvariantInfo); } else { valueStr = ojToString(); } HttpContextCurrentResponseCookies[CookieName][piName] = HttpUtilityUrlEncode(valueStr); } } //如果cookie总数超过 个 重写ASPNET_SessionId 以防Session 丢失 if (HttpContextCurrentRequestCookiesCount > && SessionCookie != null) { if (SessionCookieValue != stringEmpty) { HttpContextCurrentResponseCookiesRemove(ASPNET_SessionId); HttpContextCurrentResponseCookiesAdd(SessionCookie); } } return true; } /// /// 找回Cookie值 /// public void Load() { string cookieValue = stringEmpty; string CookieName = GetType()ToString(); //通过遍历属性 从cookie 中找回值 回写到属性 PropertyInfo[] Propertys = GetType()GetProperties(); foreach (PropertyInfo pi in Propertys) { try { cookieValue = HttpUtilityUrlDecode(HttpContextCurrentRequestCookies[CookieName][piName]ToString()); } catch { cookieValue = stringEmpty; } if (piCanWrite && cookieValue != null && cookieValue != stringEmpty) { try { object obb = cookieValue; Type type = piPropertyType; obb = ConvertChangeType(obb type); piSetValue(this obb null); } catch { } } } } #endregion } 使用 首先说明如何使用此类 为想要保存在Cookies中的类建立模型并且继承自CookieInfo即可比如下面建立了MyCookieInfo类其中包含属性pkidTestValue和TestDateTime /// /// 保存Cookies的数据对象 /// [SystemSerializable] public class MyCookieInfo : CookieInfo { private int m_Pkid = ; public int Pkid { get { return m_Pkid ; } set { m_Pkid = value ; } } private string m_TestValue = ; public string TestValue { get { return m_TestValue; } set { m_TestValue = value; } } private DateTime m_TestDateTime = DateTimeNow; public DateTime TestDateTime { get { return m_TestDateTime; } set { m_TestDateTime = value; } } } 接下来就可以使用对象的Save和Load方法保存和读取Cookies 保存 Save方法有两个重载不带参数的Save方法表示Cookies的过期时间与浏览器相同即浏览器关闭则Cookies消失否则需要传入Cookies过期时间 MyCookieInfo testCookies = new MyCookieInfo(); testCookiesPkid = ; testCookiesTestValue = 中文测试; testCookiesSave(); 读取 MyCookieInfo testCookies = new MyCookieInfo();testCookiesLoad();thislblMsgText = Pkid: + testCookiesPkidToString();thislblMsgText += TestValue: + testCookiesTestValueToString();thislblMsgText += TestDateTime: + testCookiesTestDateTimeToString(yyyy/MM/dd HH:mm:ss SystemGlobalizationDateTimeFormatInfoInvariantInfo);现在我们已经可以将一个强类型的对象读取和保存Cookies了 ()使用Javascript操作Cookies 在客户端我们同样需要操作Cookies 下面是封装了的专门用于操作Cookies的jQuery工具函数如果还有人不知道jQuery是什么请参考我的从零开始学习jQuery系列教程 l 当然此工具函数稍加修改就可以变成标准的Javascript函数 下载地址lastestjs 工具函数说明 方法签名 okie(name subName value options) 方法说明读取写入删除Cookies 方法参数 名称 说明 举例 name cookies的主键值 读取主键 okie(singleKey) 写入cookies值为字符串 okie(singleKey singleKeyvalue { expires: path: / secure: false }) subName 子键名称在写入时请传递空或者null 读取子键 okie(multiKey subName) 写入cookies值为对象 var subNameObj = { subName: aaa subName: bbb subName: ccc };okie(multiKey subNameObj { expires: path: / secure: false }); value Cookies值可以是字符串或者对象 如果是对象则将对象的每个属性保存在Cookies子键 参见上面实例 options 参数 expires可以是数字或者Data类型的对象 如果传入数字表示几天后过期 path路径默认为域名根目录(/) secure是否启用加密默认为否 指定过期时间 var myDate = new Date();myDatesetFullYear( );okie(singleKey singleKeyvalue { expires: myDate secure: false }) 天后过期 var time = Date();okie(singleKey singleKeyvalue { expires: path: / secure: false }) 五总结 很久没有发表文章了作为博客园改版后我的第一篇文章 希望对大家的工作能有所帮助 欢迎拍砖! |