青海网站维护,网络科技工作室起名,产品建站工具,襄樊网站开发原文地址#xff1a;http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.htmlCookie虽然是个很简单的东西#xff0c;但它又是WEB开发中一个很重要的客户端数据来源#xff0c;而且它可以实现扩展性很好的会话状态#xff0c; 所以我认为每个WEB开发人员都有必要对… 原文地址http://www.cnblogs.com/fish-li/archive/2011/07/03/2096903.html Cookie虽然是个很简单的东西但它又是WEB开发中一个很重要的客户端数据来源而且它可以实现扩展性很好的会话状态 所以我认为每个WEB开发人员都有必要对它有个清晰的认识。本文将对Cookie这个话题做一个全面的描述 也算是本人对Cookie的认识总结。Cookie 概述Cookie是什么 Cookie 是一小段文本信息伴随着用户请求和页面在 Web 服务器和浏览器之间传递。Cookie 包含每次用户访问站点时 Web 应用程序都可以读取的信息。为什么需要Cookie 因为HTTP协议是无状态的对于一个浏览器发出的多次请求WEB服务器无法区分 是不是来源于同一个浏览器。所以需要额外的数据用于维护会话。Cookie 正是这样的一段随HTTP请求一起被传递的额外数据。Cookie能做什么 Cookie只是一段文本所以它只能保存字符串。而且浏览器对它有大小限制以及 它会随着每次请求被发送到服务器所以应该保证它不要太大。Cookie的内容也是明文保存的有些浏览器提供界面修改所以 不适合保存重要的或者涉及隐私的内容。Cookie 的限制。 大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小最好用 Cookie 来存储少量数据或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户以及从数据库或其他数据源中读取用户信息。 浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie如果试图存储更多 Cookie则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制通常为 300 个。通过前面的内容我们了解到Cookie是用于维持服务端会话状态的通常由服务端写入在后续请求中供服务端读取。 下面本文将按这个过程看看Cookie是如何从服务端写入最后如何传到服务端以及如何读取的。Cookie的写、读过程在Asp.net中读写Cookie是通过使用HttpCookie类来完成的它的定义如下public sealed class HttpCookie
{// 获取或设置将此 Cookie 与其关联的域。默认值为当前域。public string Domain { get; set; }// 获取或设置此 Cookie 的过期日期和时间在客户端。public DateTime Expires { get; set; }// 获取一个值通过该值指示 Cookie 是否具有子键。public bool HasKeys { get; }// 获取或设置一个值该值指定 Cookie 是否可通过客户端脚本访问。// 如果 Cookie 具有 HttpOnly 属性且不能通过客户端脚本访问则为 true否则为 false。默认为 false。public bool HttpOnly { get; set; }// 获取或设置 Cookie 的名称。public string Name { get; set; }// 获取或设置要与当前 Cookie 一起传输的虚拟路径。默认值为当前请求的路径。public string Path { get; set; }// 获取或设置一个值该值指示是否使用安全套接字层 (SSL)即仅通过 HTTPS传输 Cookie。public bool Secure { get; set; }// 获取或设置单个 Cookie 值。默认值为空引用。public string Value { get; set; }// 获取单个 Cookie 对象所包含的键值对的集合。public NameValueCollection Values { get; }// 获取 System.Web.HttpCookie.Values 属性的快捷方式。public string this[string key] { get; set; }
}
Cookie写入浏览器的过程我们可以使用如下代码在Asp.net项目中写一个Cookie 并发送到客户端的浏览器为了简单我没有设置其它属性。HttpCookie cookie new HttpCookie(MyCookieName, string value);
Response.Cookies.Add(cookie);
我想很多人都写过类似的代码但是大家有没有想过Cookie最后是如何发送到客户端的呢我们打开Fiddler来看一下吧。从上图您应该能发现我们在服务端写的Cookie最后其实是通过HTTP的响应头这种途径发送到客户端的。每一个写入动作 都会产生一个【Set-Cookie】的响应头。浏览器正是在每次获取请求的响应后检查这些头来接收Cookie的。Asp.net获取Cookie的过程我们可以使用如下代码在Asp.net项目中读取一个CookieHttpCookie cookie Request.Cookies[MyCookieName];
if( cookie ! null )labCookie1.Text cookie.Value;
elselabCookie1.Text 未定义;
代码同样也很简单还是类似的问题大家有没有想过Cookie是如何传到服务端的呢我们还是继续使用Fiddler来寻找答案吧。从图片中我们可以发现Cookie是放在请求头中发送到服务端的。如果你一直刷新页面就能发现每次HTTP请求Cookie都会被发送。当然了浏览器也不是发送它所接收到的所有Cookie它会检查当前要请求的域名以及目录 只要这二项目与Cookie对应的Domain和Path匹配才会发送。对于Domain则是按照尾部匹配的原则进行的。所以我在访问 www.cnblogs.com 时浏览器并不会将我在浏览 www.163.com 所接收到的 Cookie 发出去。删除Cookie其实就是在写Cookie时设置Expires为一个【早于现在时间的时间】。也就是设置此Cookie已经过期 浏览器接收到这个Cookie时便会删除它们。HttpCookie cookie new HttpCookie(MyCookieName, null);
cookie.Expires new DateTime(1900, 1, 1);
Response.Cookies.Add(cookie);
使用Cookie保存复杂对象前面的示例代码大致演示了Cookie的读写操作。不过我们平时可能希望将更复杂的【自定义类型】通过Cookie来保存 那么又该如何操作呢对于这个问题我们定义一个类型来看看如何处理。public class DisplaySettings
{public int Style;public int Size;public override string ToString(){return string.Format(Style {0}, Size {1}, this.Style, this.Size);}
}
上面的代码我定义一个类型用于保存用户在浏览页面时的显示设置。接下来我将介绍二种方法在Cookie中保存并读取它们。方法-1经典做法。注意前面给出的HttpCookie定义代码中的最后二个成员private void WriteCookie_2a()
{DisplaySettings setting new DisplaySettings { Style 1, Size 24 };HttpCookie cookie new HttpCookie(DisplaySettings1);cookie[Style] setting.Style.ToString();cookie[Size] setting.Size.ToString();Response.Cookies.Add(cookie);
}private void ReadCookie_2a()
{HttpCookie cookie Request.Cookies[DisplaySettings1];if( cookie null )labDisplaySettings1.Text 未定义;else {DisplaySettings setting new DisplaySettings();setting.Style cookie[Style].TryToInt();setting.Size cookie[Size].TryToInt();labDisplaySettings1.Text setting.ToString();}
}
方法-2将对象JSON序列化为字符串。private void WriteCookie_2b()
{DisplaySettings setting new DisplaySettings { Style 2, Size 48 };HttpCookie cookie new HttpCookie(DisplaySettings2, setting.ToJson());Response.Cookies.Add(cookie);
}private void ReadCookie_2b()
{HttpCookie cookie Request.Cookies[DisplaySettings2];if( cookie null )labDisplaySettings2.Text 未定义;else {DisplaySettings setting cookie.Value.FromJsonDisplaySettings();labDisplaySettings2.Text setting.ToString();}
}
这段代码使用了我定义的二个扩展方法。点击此处展开/// summary
/// 将一个对象序列化成 JSON 格式字符串
/// /summary
/// param nameobj/param
/// returns/returns
public static string ToJson(this object obj)
{if( obj null )return string.Empty;JavaScriptSerializer jss new JavaScriptSerializer();return jss.Serialize(obj);
}/// summary
/// 从JSON字符串中反序列化对象
/// /summary
/// typeparam nameT/typeparam
/// param namecookie/param
/// returns/returns
public static T FromJsonT(this string cookie)
{if( string.IsNullOrEmpty(cookie) )return default(T);JavaScriptSerializer jss new JavaScriptSerializer();return jss.DeserializeT(cookie);
}
对于这二种方法我个人更喜欢后者因为它具有更好扩展性如果类型增加了成员不需要修改读写Cookie的代码。不过这种方式产生的有些字符比如【双引号】极少数浏览器(Opera)不支持所以需要做UrlEncode或者Base64编码处理。同理对于第一种方法遇到Value有【双引号】时我们同样需要做UrlEncode或者Base64编码处理。Js中读写CookieCookie并非只能在服务端读写在客户端的浏览器中也可以实现对它的读写访问。而且在JS中创建的Cookie对于服务端仍然有效可见 接下来我们来看看在JS中如何写入Cookie演示代码将创建一个按钮并在点击按钮后写入Cookieinput typebutton onclickWriteCookie(); valueWriteCookie /script typetext/javascriptfunction WriteCookie() {var cookie cookie_js22222222; path/;document.cookie cookie;}
/script
在JS中写Cookie很简单只要给document.cookie赋值一个Cookie字符串即可至于格式可以参考前面用Fiddle看到的结果。再来看一下如何使用JS读取Cookie吧。请参考如下代码input typebutton onclickReadCookie(); valueReadCookie /script typetext/javascriptfunction ReadCookie() {alert(document.cookie);}
/script
仍然是访问document.cookie不过这次我们得到却是全部的Cookie值每个Key/Value项用分号分开中间则用等号分开。 所以 如果您想在JS中读取Cookie一定要按照这个规则来拆分并解析您要读取的Cookie项。鉴于这样的操作有些繁琐 我们可以jquery.cookie.js插件来轻松完成这个功能有兴趣的朋友也可以看一下它是如何处理的。 这个插件的代码比较少这里就直接贴出点击此处展开/*** Create a cookie with the given name and value and other optional parameters.** example $.cookie(the_cookie, the_value);* desc Set the value of a cookie.* example $.cookie(the_cookie, the_value, {expires: 7, path: /, domain: jquery.com, secure: true});* desc Create a cookie with all available options.* example $.cookie(the_cookie, the_value);* desc Create a session cookie.* example $.cookie(the_cookie, null);* desc Delete a cookie by passing null as value.** param String name The name of the cookie.* param String value The value of the cookie.* param Object options An object literal containing key/value pairs to provide optional cookie attributes.* option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.* If set to null or omitted, the cookie will be a session cookie and will not be retained* when the the browser exits.* option String path The value of the path atribute of the cookie (default: path of page that created the cookie).* option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).* option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will* require a secure protocol (like HTTPS).* type undefined** name $.cookie* cat Plugins/Cookie* author Klaus Hartl/klaus.hartlstilbuero.de*//*** Get the value of a cookie with the given name.** example $.cookie(the_cookie);* desc Get the value of a cookie.** param String name The name of the cookie.* return The value of the cookie.* type String** name $.cookie* cat Plugins/Cookie* author Klaus Hartl/klaus.hartlstilbuero.de*/
jQuery.cookie function(name, value, options) {if (typeof value ! undefined) { // name and value given, set cookieoptions options || {};if (value null) {value ;options.expires -1;}var expires ;if (options.expires (typeof options.expires number || options.expires.toUTCString)) {var date;if (typeof options.expires number) {date new Date();date.setTime(date.getTime() (options.expires * 24 * 60 * 60 * 1000));} else {date options.expires;}expires ; expires date.toUTCString(); // use expires attribute, max-age is not supported by IE}var path options.path ? ; path options.path : ;var domain options.domain ? ; domain options.domain : ;var secure options.secure ? ; secure : ;document.cookie [name, , encodeURIComponent(value), expires, path, domain, secure].join();} else { // only name given, get cookievar cookieValue null;if (document.cookie document.cookie ! ) {var cookies document.cookie.split(;);for (var i 0; i cookies.length; i) {var cookie jQuery.trim(cookies[i]);// Does this cookie string begin with the name we want?if (cookie.substring(0, name.length 1) (name )) {cookieValue decodeURIComponent(cookie.substring(name.length 1));break;}}}return cookieValue;}
};
注意哦前面我们看到了HttpCookie有个HttpOnly属性如果它为true那么JS是读不到那个Cookie的也就是说 我们如果在服务端生成的Cookie不希望在JS中能被访问可以在写Cookie时设置这个属性。不过通过一些工具还是可以看到它们。接下来我们再来看看Asp.net中Cookie有哪些应用。Cookie在Session中的应用在Asp.net中HttpContext, Page对象都有个Session的对象我们可以使用它来方便地在服务端保存一些与会话相关的信息。前面我们也提到过HTTP协议是无状态的对于一个浏览器发出的多次请求WEB服务器无法区分 是不是来源于同一个浏览器。所以为了实现会话服务端需要一个会话标识ID能保存到浏览器让它在后续的请求时都带上这个会话标识ID以便让服务端知道 某个请求属于哪个会话这样便可以维护与会话相关的状态数据。由于Cookie对于用户来说是个不可见的东西而且每次请求都会传递到 服务端所以它就是很理想的会话标识ID的保存容器。在Asp.net中默认也就是使用Cookie来保存这个ID的。注意虽然Asp.net 2.0也支持无Cookie的会话但那种方式要修改URL也有它的缺点因此这种方法并没有广泛的使用。本文将不对这个话题做过多的分析 就此略过无Cookie会话这种方式。我们来看看Session是如何使用Cookie来保存会话标识ID的在默认的Asp.net配置中Web.config有着如下定义sessionState modeInProc cookieNameASP.NET_SessionId cookielessUseCookies/sessionState如果我们执行以下操作Session[Key1] DateTime.Now;此时我们可以使用一些浏览器提供的工具来查看一下现在的Cookie情况。从图片上看这个Cookie的名字就是我们在配置文件中指出的名称我们可以修改一下配置文件sessionState cookieNameSK/sessionState再来执行上面的写Session的操作然后看Cookie我们可以看到SK的Cookie出现了。说明在截图时我把名称为ASP.NET_SessionId的Cookie删除了。通过上面示例我们可以得到结论Session的实现是与Cookie有关的服务端需要将会话标识ID保存到Cookie中。这里再一次申明除非你使用无Cookie的会话模式否则Session是需要Cookie的支持。反过来Cookie并不需要Session的支持。Cookie在身份验证中的应用我想很多人都在Asp.net的开发中使用过Form身份认证。对于一个用户请求 我们可以在服务端很方便地判断它是不是代表一个已登录用户。this.labStatus.Text (Request.IsAuthenticated ? 已登录 : 未登录);那么您有没有好奇过Asp.net是如何识别一个请求是不是一个已登录用户发起的呢说到这里我们就要从用户登录说起了。 为了实现登录及Form认证方式我们需要如下配置authentication modeForms forms nameUserStatus/forms
/authentication
接下来我们需要实现用户登录逻辑。具体实现方式有很多不过最终的调用都是差不多的如下代码所示private void SetLogin()
{System.Web.Security.FormsAuthentication.SetAuthCookie(fish, false);
}
只要执行了以上代码我们就可以看到前面的判断【Request.IsAuthenticated】返回true最终会显示已登录。 为了探寻这个秘密我们还是来看一下当前页面的Cookie情况。果然多出来一个Cookie名称与我在配置文件中指定的名称相同。我们再来看看如果注销当前登录会是什么样子的private void SetLogout()
{System.Web.Security.FormsAuthentication.SignOut();
}
看到了吗名为UserStatus的Cookie不见了。此时如果你再去观察【Request.IsAuthenticated】可以发现它此时返回 false。 或者您也可以再试一次登录后直接删除名为UserStatus的Cookie也能发现登录状态将显示未登录。 或许您还是有点不清楚前面我调用【System.Web.Security.FormsAuthentication.SetAuthCookie(fish, false);】后Asp.net做了些什么 回答这个问题其实很简单自己用Reflector.exe去看一下Asp.net的实现吧。这里为了更让您能信服登录与Cookie有关我将直接创建一个Cookie看一下Asp.net能不能认可我创建的Cookie并认为登录有效。请看代码private void SetLogin()
{//System.Web.Security.FormsAuthentication.SetAuthCookie(fish, false);// 下面的代码和上面的代码在作用上是等效的。FormsAuthenticationTicket ticket new FormsAuthenticationTicket(2, fish, DateTime.Now, DateTime.Now.AddDays(30d), false, string.Empty);string str FormsAuthentication.Encrypt(ticket);HttpCookie cookie new HttpCookie(FormsAuthentication.FormsCookieName, str);Response.Cookies.Add(cookie);
}
如果执行这段代码您将发现【Request.IsAuthenticated】返回true登录状态会显示已登录。至此我们可以得出一个结论Form身份认证依赖CookieAsp.net就是每次检查我们在配置文件中指定的Cookie名称并解密这个Cookie来判断当前请求用户的登录状态。Cookie的安全状况从以上图片您应该能发现浏览器能提供一些界面让用户清楚的观察我们在服务端写的Cookie 甚至有些浏览器还提供很方便的修改功能。如下图所示所以我们在服务端写代码读取Cookie时尤其是涉及类型转换、反序列化或者解密时一定要注意这些操作都有可能会失败。 而且上图也清楚的反映了一个事实Cookie中的值都是“一目了然”的任何人都能看到它们。所以我们尽量不要直接在Cookie中 保存一些重要的或者敏感的内容。如果我们确实需要使用Cookie保存一些重要的内容但又不希望被他人看懂 我们可以使用一些加密的方法来保护这些内容。1. 对于一些重要性不高的内容我们可以使用Base64之类的简单处理方式来处理。2. 对于重要性相对高一点的内容我们可以利用.net提供的一些加密工具类自己来设计加密方法来保护。不过 密码学与加密解密并不是很简单的算法因此自己设计的加密方式可能不会很安全。3. 重要的内容我们可以使用.net提供的FormsAuthenticationTicket,FormsAuthentication来加密。我认为这种方式还是比较安全的。 毕竟前面我们也看过了Asp.net的Form身份认证就是使用这种方式来加密用户登录的身份标识的所以如果这种方式不安全 也就意味着Asp.net的身份认证也不安全了。 如果您使用这种方式来加密那么请注意它产生的加密后文本还是比较大的 前面我也提到过每次请求时浏览器都会带上与请求相匹配的所有Cookie因此这种Cookie会对传输性能产生一定的影响 所以请小心使用切记不可过多的使用。这里要补充一下去年曾经出现过【Padding Oracle Attack】这个话题 一些人甚至错误的认为是Asp.net加密方式不安全如果您也是这样认为的那么可以看一下这篇文章浅谈这次ASP.NET的Padding Oracle Attack相关内容以消除这个错误的认识。当然了我们也可以从这个话题得到一些收获解密失败时不要给出过多的提示就当没有这个Cookie存在。如何在C#发请的请求中使用Cookie前面我们一直在谈服务端与浏览器中使用Cookie其实浏览器也是一个普通的应用程序.net framework也提供一些类也能让我们 直接发起HTTP请求下面我们来看一下如何在C#发请的请求中使用Cookie 其实也很简单主要是使用了CookieContainer类请看以下演示代码private static string SendHttpRequestGet(string url, Encoding encoding, CookieContainer cookieContainer){if( string.IsNullOrEmpty(url) )throw new ArgumentNullException(url);if( encoding null )throw new ArgumentNullException(encoding);HttpWebRequest request (HttpWebRequest)WebRequest.Create(url);request.Method GET;request.CookieContainer cookieContainer;using( WebResponse response request.GetResponse() ) {using( StreamReader reader new StreamReader(response.GetResponseStream(), encoding) ) {return reader.ReadToEnd();}}}private void SendHttpDEMO(){StringBuilder sb new StringBuilder();CookieContainer cookieContainer new CookieContainer();string url http://www.taobao.com;SendHttpRequestGet(url, Encoding.Default, cookieContainer);// 后面可以继续发起HTTP请求此时将会包含上次从服务器写入的Cookie//SendHttpRequestGet(同域名下的其它URL, Encoding.Default, cookieContainer);// 至此我们可以显示取得了哪些CookieCookieCollection cookies cookieContainer.GetCookies(new Uri(url));if( cookies ! null ) {foreach( System.Net.Cookie cookie in cookies )sb.AppendLine(cookie.ToString());}txtCookies.Text sb.ToString();}
重构与使用总结在前面的Asp.net示例代码中我一直使用.net提供的HttpCookie类来操作Cookie是为了展示用原始的方式来使用Cookie 这些代码有点重复也有点繁琐 为此我提供了几个简单的方法可以更容易的使用Cookie也算是对Cookie使用的一个总结。/// summary
/// 用于方便使用Cookie的扩展工具类
/// /summary
public static class CookieExtension
{// 我们可以为一些使用频率高的类型写专门的【读取】方法/// summary/// 从一个Cookie中读取字符串值。/// /summary/// param namecookie/param/// returns/returnspublic static string GetString(this HttpCookie cookie){if( cookie null )return null;return cookie.Value;}/// summary/// 从一个Cookie中读取 Int 值。/// /summary/// param namecookie/param/// param namedefaultVal/param/// returns/returnspublic static int ToInt(this HttpCookie cookie, int defaultVal){if( cookie null )return defaultVal;return cookie.Value.TryToInt(defaultVal);}/// summary/// 从一个Cookie中读取值并转成指定的类型/// /summary/// typeparam nameT/typeparam/// param namecookie/param/// returns/returnspublic static T ConverToT(this HttpCookie cookie){if( cookie null )return default(T);return (T)Convert.ChangeType(cookie.Value, typeof(T));}/// summary/// 从一个Cookie中读取【JSON字符串】值并反序列化成一个对象用于读取复杂对象/// /summary/// typeparam nameT/typeparam/// param namecookie/param/// returns/returnspublic static T FromJsonT(this HttpCookie cookie){if( cookie null )return default(T);return cookie.Value.FromJsonT();}/// summary/// 将一个对象写入到Cookie/// /summary/// param nameobj/param/// param namename/param/// param nameexpries/parampublic static void WriteCookie(this object obj, string name, DateTime? expries){if( obj null )throw new ArgumentNullException(obj);if( string.IsNullOrEmpty(name) )throw new ArgumentNullException(name);HttpCookie cookie new HttpCookie(name, obj.ToString());if( expries.HasValue )cookie.Expires expries.Value;HttpContext.Current.Response.Cookies.Add(cookie);}/// summary/// 删除指定的Cookie/// /summary/// param namename/parampublic static void DeleteCookie(string name){if( string.IsNullOrEmpty(name) )throw new ArgumentNullException(name);HttpCookie cookie new HttpCookie(name);// 删除Cookie其实就是设置一个【过期的日期】cookie.Expires new DateTime(1900, 1, 1);HttpContext.Current.Response.Cookies.Add(cookie);}
}更完整的代码可以从本文的示例代码中获得。文章底部有下载地址使用方式public static class TestClass
{public static void Write(){string str 中国;int aa 25;DisplaySettings setting new DisplaySettings { Style 3, Size 50 };DateTime dt new DateTime(2012, 1, 1, 12, 0, 0);str.WriteCookie(Key1, DateTime.Now.AddDays(1d));aa.WriteCookie(Key2, null);setting.ToJson().WriteCookie(Key3, null);dt.WriteCookie(Key4, null);}public static void Read(){HttpRequest request HttpContext.Current.Request;string str request.Cookies[Key1].GetString();int num request.Cookies[Key2].ToInt(0);DisplaySettings setting request.Cookies[Key3].FromJsonDisplaySettings();DateTime dt request.Cookies[Key4].ConverToDateTime();}
}注意哦以上代码中都是直接使用字符串Key的形式这种方式对于大一些的程序在后期可能会影响维护。所以建议将访问Cookie所使用的Key能有一个类来统一的定义或者将读写操作包装成一些属性放在一个类中统一的管理。public static class CookieValues
{// 建议把Cookie相关的参数放在一起提供 get / set 属性或者方法来访问以避免key到处乱写public static string AAA{get { return HttpContext.Current.Request.Cookies[Key1].GetString(); }}public static int BBB{get { return HttpContext.Current.Request.Cookies[Key2].ToInt(0); }}public static DisplaySettings CCC{get { return HttpContext.Current.Request.Cookies[Key3].FromJsonDisplaySettings(); }}public static DateTime DDD{get { return HttpContext.Current.Request.Cookies[Key4].ConverToDateTime(); }}
}
好了就到这里吧。本文的所有示例代码可以点击此处下载。二天时间写此文希望能给您一点收获。如果认为此文对您有帮助别忘了支持一下哦。转载于:https://www.cnblogs.com/fcsh820/archive/2011/07/06/2098774.html