1. 为什么会出现cookie和session?
? 先看一个例子:
我们还是使用之前的NVelocity,不清楚的参看链接:http://blog.csdn.net/u010955843/article/details/42977533,同样建立commonhelper类,里面封装NVelocity模板,并且建立html也作为渲染的模板进行显示。
html页
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;"><!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form action="../zhuangtaibaochiHttpNoState.ashx" method="get"> <input type="submit" value="增加" /> <p>$Model.Count</p> </form> </body> </html> </span></strong></strong></span>
一般处理程序
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;">using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace zhuangtaibaochi { /// <summary> /// zhuangtaibaochiHttpNoState 的摘要说明 /// </summary> public class zhuangtaibaochiHttpNoState : IHttpHandler { private int i=1; public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/html"; context.Response.Write("Hello World"); i++; string html = commonHelper.RenderHtml("HttpNozhuangtai.html",new{ Count=i}); context.Response.Write(html); } public bool IsReusable { get { return false; } } } }</span></strong></strong></span>
commonhelper修改一处即可,其实可以不改,自己设计的填充模板的数据的名字,可以随便设计
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;">using NVelocity; using NVelocity.App; using NVelocity.Runtime; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace zhuangtaibaochi { public class commonHelper { /// <summary> /// 用data数据填充templateName模板,渲染生成html返回 /// </summary> /// <param name="templateName">模板名字</param> /// <param name="data">填充模板数据</param> /// <returns></returns> public static string RenderHtml(string templateName, object data) { VelocityEngine vltEngine = new VelocityEngine(); vltEngine.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file"); vltEngine.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, System.Web.Hosting.HostingEnvironment.MapPath("~/templates"));//模板文件所在的文件夹,我们遵从上次讲的我们还是放在templates这个文件夹中,自己也可以新建别的名字的 vltEngine.Init(); //引擎初始化 VelocityContext vltContext = new VelocityContext(); vltContext.Put("Model", data);//设置参数,在模板中可以通过$data来引用 Template vltTemplate = vltEngine.GetTemplate(templateName);//渲染的html模板 System.IO.StringWriter vltWriter = new System.IO.StringWriter(); vltTemplate.Merge(vltContext, vltWriter); string html = vltWriter.GetStringBuilder().ToString(); return html; } } }</span></strong></strong></span>
我们发现即使每次点击增加按钮,数字2都不会改变,原因在于http协议是无状态的。可以自己试试。
? 再看一个例子:通过隐藏字段让http记住状态
同上例,只是修改html页和一般处理程序,commhelper不需要改变
html页
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> </head> <body> <form action="HtttpNoZhuangTai.ashx" method="get"> <input type="submit" value="增加" /> <input type="hidden" name="Count" value="$Model.Count" /> <p>$Model.Count</p> </form> </body> </html> </span></strong></strong></span>
一般处理程序,只是修改ProcessRequest方法
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;"> private int i=1; public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/html"; //context.Response.Write("Hello World"); //i++; //string html = commonHelper.RenderHtml("HttpNozhuangtai.html",new{ Count=i}); //context.Response.Write(html); //从病历本读出上次的值 int count = Convert.ToInt32(context.Request["Count"]); count++; //新的值写入病历本 string html = commonHelper.RenderHtml("HttpNozhuangtai.html", new { Count = count }); context.Response.Write(html); }</span></strong></strong></span>
此时当我们点击的时候发现实现了每次数目的递增,这是因为我们将值存储在了隐藏字段里面,这样需要的时候可以方便地调出来。比如我们可以把客户端假定为病人,服务端看成是医生,我们知道当医生诊断完一个患者,会给病人一个病历本,这样下次来看的时候就会根据上次的诊断情况来确定这次如何去诊断,让客户端(病人)帮你保存字段,此时隐藏字段相当于病历本,隐藏字段记住了之前的状态,当需要的时候再次调出,这样就实现了数据递增。
但是我们因为是get提交,用户是可以更改信息,这样就会出现信息的不安全性,所以存放在客户端的必须是用户更改之后页无所谓的,否则存在很大的问题。
? 无状态http协议简介
http协议是无状态的,不会记得上次和网页“发生了什么”。服务器不记的上次给了浏览器什么,否则服务器的压力太大,浏览器需要记住这些值,下次再提交服务器的时候,就要把上次的值交给服务器,让他想起来。如果要知道上一次的状态,一个方法是在对浏览器响应结束之前将状态信息保存到页面表单总(实现一下),下次页面再向服务器发出请求的时候带上这些状态信息,这样服务器就能根据这些状态信息还原上次的状态了,类似于去看病的病历本。
尽量不要这么干(将信息存储在服务端),客户端的事情让客户端去做。
状态信息保存在隐藏字段中的缺点:加大网站的流量、降低访问速度、机密数据放到表单中会有数据欺骗等安全性问题。
2. Cookie
隐藏字段实现不了自由传递数据和读取数据,只有需要时候发送请求,之后服务器再响应。故而cookie应运而生。
? 什么是cookie?
如果想要实现自由的传递和读取,用cookie。Cookie是和站点相关的,并且每次向服务器请求的时候除了发送表单参数外,还会将和站点相关的所有的cookie都提交给服务器,是强制性的。Cookie也是保存在浏览器端的,而且浏览器会在每次请求的时候都会把和这个站点相关的cookie提交到服务器,并且将服务端返回的cookie更新回数据库,因此可以将信息保存在cookie中,然后在服务器端读取和修改。服务器返回数据除了普通的html数据以外,还会返回修改的cookie,浏览器把拿到的cookie值更新本地浏览器的cookie就可以。
此外存到浏览器中,也就是本地电脑中,是可读可写的,只有网站自己可以读写的,别人是读不了的,避免了隐私外泄的安全隐患。也就是与域名(域名不同,网站不同)相关的,本地域名或者自己的域名才能读,谁写谁读;存到本地硬盘中,浏览器通过报文字段发过去,每次向服务器请求,都会发送cookie,浏览器在后台通过报文字段进行发送。
? 示例
新建一般处理程序名为cookieTest.ashx
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;">using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace zhuangtaibaochi { /// <summary> /// cookieTest 的摘要说明 /// </summary> public class cookieTest : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/html"; //context.Response.Write("Hello World"); string read=context.Request["Read"]; if (!string.IsNullOrEmpty(read)) { HttpCookie cookie = context.Request.Cookies["Age"]; string value = cookie.Value; var data = new { Value = value }; string html = commonHelper.RenderHtml("cookieTest.html", data); context.Response.Write(html); } else if (!string.IsNullOrEmpty(context.Request["Write"])) { context.Response.SetCookie(new HttpCookie("Age", "33")); string html = commonHelper.RenderHtml("cookieTest.html", null); context.Response.Write(html); } else { string html = commonHelper.RenderHtml("cookieTest.html", null); context.Response.Write(html); } } public bool IsReusable { get { return false; } } } }</span></strong></strong></span>
html页
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<form action="../cookieTest.ashx" method="post">
<input type="submit" name="Read" value="读" />
<input type="submit" name="Write" value="写" />
读出来的cookie:$Model.Value
</form>
</body>
</html>
commonhelper同上面的
这样我们便可以读取cookie的值。
? 报文分析
运行程序,并且打开firebug(火狐浏览器)其他浏览器中快捷键F12或者工具菜单下面的开发人员工具均可以查看。
此时刷新页面可以看到我们发送的请求:
这时候我们可以点击写,因为此时没有cookie我们需要存入一个cookie
此时设置课cookie,存在浏览器的某个地方;下次再发请求的时候直接读取cookie即可,此时执行的是处理程序的读方法,也就是可以在服务器端通过request.cookie进行获取了。此时点击读显示
Cookie在客户端是以键值对的形式存在的,告诉cookie进行存储
点击写按钮的时候执行写进一个cookie,当点击读的时候将所存储的cookie进行读出来的操作,读取的时候通过报文进行读取,利用报文进行读取的和传递的
? 限制
Cookie的缺点是不能存储过多的信息,机密信息不能存;Cookie无法跨不同的浏览器,Cookie存在客户端,故而存在安全性的问题,cookie是可以被清除的,不能把不能丢的数据存到cookie中;cookie尺寸有限制,一般为几k或者几百k。
如何清除Cookis,我们以ie为例
点击工具—Internet选项
点击删除时显示
? 过期问题
浏览器关闭之后那么cookie就会过期不再存在(默认情况下),如何使得浏览器关闭之后仍可以存在呢?
设置过期日期是一天后的,也就是一天后的这个时间
在上述的例子上修改cookieTest.ashx中的部分代码即可
<span style="font-family:Microsoft YaHei;"><strong><strong><span style="font-family:Microsoft YaHei;font-size:14px;">else if (!string.IsNullOrEmpty(context.Request["Write"])) { //SetCookie(new HttpCookie("Age", "33")) Cookie生命周期是浏览器的生命周期 //浏览器重启就自动删除了 //context.Response.SetCookie(new HttpCookie("Age", "33")); HttpCookie cookie1 = new HttpCookie("Age", "33"); cookie1.Expires = DateTime.Now.AddDays(1); context.Response.SetCookie(cookie1); context.Response.SetCookie(new HttpCookie("Name", "yzk")); string html = commonHelper.RenderHtml("cookieTest.html", null); context.Response.Write(html); } else { string html = commonHelper.RenderHtml("cookieTest.html", null); context.Response.Write(html); }</span></strong></strong></span>
此时点击写查看报文信息,看到了服务器端响应的被设置的cookie以及过期日期,过期之后的新的cookie
如果浏览器满了就会根据优先级或者最先存的删除掉了,过期时间是最多,并不是肯定就是那个日期。
不设置cookie的失效时间,默认的就是关闭浏览器就会使得其失效,这样都不安全,因为客户端用户可以修改cookie的值。Cookie相当于病历本,写到病历本,用户不会篡改cookie。也就是用户不会篡改病历本(把自己的病情写成别的,但是不一定),也就是存在修改,是不安全的。同时用户会担心医生修改病历本,故而这样写到病历本上的信息就不安全。如何防止病人修改或者医生修改或者家属修改修改病历本呢?
Cookie不能存储机密数据,如果想保存机密数据,可以保存一个Guid(全局唯一标识符(GUID,Globally Unique Identifier)也称作 UUID(Universally Unique IDentifier) 。GUID是一种由算法生成的二进制长度为128位的数字标识符。GUID主要用于在拥有多个节点、多台计算机的网络或系统中。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。GUID
的总数达到了2^128(3.4×10^38)个,所以随机生成两个相同GUID的可能性非常小,但并不为0。)到Cookie中,然后在服务器端建建立一个以Guid为Key,复杂数据为Value全局Dictionary。
3. 总结
存到客户端浏览器是很容易修改的,故而存在安全隐患,所以引出来了服务器端的机制,那就没有办法修改了,故而服务器端的数据安全性较客户端好的多,并且在其他的页面也能取到数据。
下篇博客我们将继续进行另一种较为安全的服务器端的机制那就是session的讲解,请继续关注~