计算机网络(HTTP)之客户识别:cookie机制
- 什么是cookie?
- 承载用户相关信息的HTTP首部
- cookie的工作原理
- cookie的缺陷
一、什么是cookie?
cookie是由服务器生成,发送给USER-Agent(一般是浏览器),(服务器告诉浏览器设置一下cookie),浏览器会将cookie以key/value保存到某个目录下的文本文件内,下次请求同一个网站时就发送该cookie给服务器(前提是浏览器设置为启用cookie)。cookie就是一个小型文件(浏览器对cookie的内存大小有限制)。
cookie可以做什么?
1.服务器可以通过cookie在客户端缓存一些标识客户特性的信息,比如通过个性化标识为客户提供个性化体验。比如推送客户感兴趣的资讯和商品。
2.服务器可以通过cookie缓存客户的身份信息,可以让客户在下次访问该网站时自动登入,提高交互体验。
3.HTTP是无状态的,每条请求/响应都是独立进行的,web站点有时候需要有一种方式来区分不同用户的HTTP事务。
二、承载用户信息的HTTP头部?
HTTP协议识别用户的几种机制:客户端IP地址跟踪;用户认证识别;胖URL信息识别;cookie持久身份识别技术。这些识别技术分别对应着一些HTTP的首部信息,下表展示了承载用户信息的HTTP首部,以及后面详细的具体用户识别与这些首部的关系。
首部名称 | 首部类型 | 描述 |
From | 请求 | 用户的E-mail地址 |
User-Agent | 请求 | 用户的浏览器软件 |
Referer | 请求 | 用户是从这个页面上依照连接跳转过来的 |
Authorization | 请求 | 用户和密码 |
Client-IP | 扩展请求 | 客户端的IP地址 |
X-Forwarded-For | 扩展请求 | 客户端的IP地址 |
Cookie | 扩展请求 | 服务器产生的ID标签 |
1.From首部包含了用户的E-mail地址:服务器通过From首部包含的E-mail地址来识别用户。但由于有很有不道德的服务器收集这些E-mail用来散发垃圾邮件,所以很少有浏览器会发送From首部。实际上From首部是由自动化的机器人(蜘蛛)发送。
2.User-Agent首部可以将用户所用的浏览器的相关信息告知服务器,包括程序的名称和版本。
3.Referer首部提供用户来源页面的URL。(比如从一个篮球俱乐部的网站跳到某个web服务器,这个服务器会推断你是一个篮球迷)
4.客户端的IP地址:HTTP首部并不承载客户端的IP地址,但web服务器可以找到承载HTTP请求的TCP连接上的IP地址。(客户端IP地址描述的是所用的机器,而不是用户。如果多个用户共享一台计算机就无法对其区分;很多因特网服务商会动态分配IP地址,因此用户每次登入的IP可能都会不同;还有为了提高安全性,用户都是通过网络地址转换防火墙来浏览网络内容,这些NAT设备隐藏了防火墙后面哪些实际客户端的IP地址,将实际的IP地址转换成一个共享的防火墙IP地址和不同的端口号;HTTP代理通常会打开新的、到原始服务器的TCP连接,web服务器看到的将是代理服务器的IP地址,而不是客户端的。有些代理为了绕开这个问题会添加特殊的Client-IP或X-Forwarded-For扩展首部来保存原始的IP地址,但不是所有代理都支持这种行为。)
5.用户登入——用户认证识别:HTTP中包含一种内建机制,可以用WWW-Authenticate首部和Authorization首部向web站点传送用户的相关信息。一旦登入,浏览器就可以不断地在每条发往这个站点的请求中发送这个登入信息,这样就总有登入信息可用。在后用具体的HTTP认证机制进行更加详细的讨论。(大概的过程是:当用户通过浏览器访问某个站点时,服务器不知道这个用户的身份会返回401Login Required HTTP响应码,并添加WWW-Authenticate首部,要求用户登入。浏览器弹出登入框。只要用户输入用户名和密码,浏览器就会重复原来的请求。这个添加一个Authorization首部,说明用户名和密码,并且进行加密。今后的请求使用用户名和密码时,浏览器会自动将存储下来的值发送出去,甚至在站点没有要求发送时,也会经常向其发送。浏览器在每次请求中都向服务器发送Authorization首部作为一种身份的标识,这样,只要登入一次,就可以在整个会话期间维持用户的身份。)
6.胖URL:Web站点为每个用户生成特定版本的URL来追踪用户身份,比如当用户访问某个站点时,该站点的服务器在响应报文的URL后面添加一个随机数来标识这个用户。这种识别用户的方式也会存在很多问题,比如无法共享URL,不然就会将用户积累的个人信息都共享出去;破坏缓存,为每个URL生成用户特有的版本,就意味着不再有可供公共访问的URL需要缓存了;逃逸口,当用户跳转到其他站点或者特定URL时,就很容易“逃离”胖URL会话;在绘画间是非持久的,除非用户收藏了胖URL,否则用户推出登入时,所有信息都会丢失。
三、cookie的工作原理
1.cookie的属性:不同的浏览器会以不同的方式来存储cookie,网景的Navigator会将cookie存储在一个名为cookies.txt的文本文件中。通过这个文本的数据结构来了解cookie数据的属性。(查看cookie的方法:控制台-->Application-->cookies)
#Netscape HTTP Cookie File | ||||||
#HTTP://www.netscape.com/newsref/std/cookie_spec.html | ||||||
#This is a generated file!Do not edit. | ||||||
# | ||||||
#domain | allh | path | secure | expiration | name | value |
www.fedex.com | FALSE | / | FALSE | 1136109676 | cc | /us/ |
secure.eepulse.net | FALSE | /eePulse | FALSE | 1007162968 | cid | %FE%FF%002 |
... |
domain(域):cookie的域。
allh:是域中所有的主机获取cookie,还是只有指定了名字的主机获取。
path(路径):域中与cookie相关的路径前缀。
secure(安全):是否只有在使用SSL连接时才发送这个cookie。
expiration(过期):从格林尼治标准时间1970年1月1日00:00:00开始的cookie过期的秒数。
name(名称):cookie变量的名称。
value(值):cookie变量的值。
2.cookie的使用权限:只有访问对应的站点才能携带对应站点的cookie信息作为HTTP首部发送给服务器,甚至对应的路径。
3.cookie的设置:服务器在响应报文中使用Set-Cookie:"name=value"向浏览器中添加cookie信息。cookie的属性也可以使用HTTP响应报文首部设置,比如可以这样设置域(domin="baidu.com"),如果不设置就是默认为当前访问的域。通过expiration设置cookie的生命周期,等其他cookie属性都可以在响应报文首部设置。
4.cookie信息什么时候发送给服务器?当用户访问某个站点时,浏览器在发送请求前会到cookies.txt文件中查询是否存在该站点的cookie信息,如果存在会在HTTP请求首部添加首部信息:cookie:"name=value",如果存在多个就会在HTTP请求中添加多个首部。
5.cookie文件有大小限制:每个浏览器都有些不同,不过大概都在4090字节左右,每个域名存放的cookie个数差别就比较大了,每个cookie大小也有限制,大约4K左右。
6.cookie的设置除了使用响应报文的方式也可以直接通过JS代码设置:
document.cookie;//获取当前域全部cookie信息 document.cookie = "id=00001"; console.log(document.cookie);//id=00001
在控制台查看:
同样也可以设置cookie的属性:
document.cookie = "id=00001;max-age=1000;";//设置cookie存储时间1000秒,但是其时间是格林威治时间 document.cookie = "name=xiaoming; max-age=session;"//将max-age设置为session,就表示其为临时cookie,当用户将所有当前域的浏览器窗口关闭这个cookie就会删除
有趣的是我的浏览器显示的却是这样的:
被设置为session的cookie的时间却是1969年12月31日23时59分59秒,在有的浏览器中显示的就是session,但是这两者没有什么区别,都表示为临时cookie,反而显示时间为1969年的更反应了cookie的内部机制,浏览器是更具当前时间与cookie时间来判断其是否过期,如果过期浏览器就会将其删除,如果时间都在计算机元年之前了,当浏览器关闭窗口时肯定被瞬间秒杀了。
既然直接设置秒数的时间是在计算机元年上加上设置的时间,那怎么来设置cookie预期的过期时间呢?可以直接使用Date对象作为时间值,但是值得注意的是如果采用Date对象就不能使用max-age了,而是使用expires。
var date = new Date(); date.setDate(date.getDate()+3);//设置cookie过期时间是三天后 document.cookie = "cc=CC; expires=" + date +";";
所以,时间的设置又有了新的意义,如果某个cookie在过期之前因为业务的变化而过时了,而且cookie的个数有限,但是新的业务又需要设置新的cookie怎么办呢?将之间设置为过期时间就OK了啊。比如上面的cc="CC":
document.cookie="cc=CC;max-age=-1;";
6.封装一个cookie添加删除的API:
1 var manageCookie = { 2 //name,alue, date(expires), path, domain, secure 3 //名称/值, 过期时间, 路径, 域名, 安全 4 setCookie:function(name,value,date,path,domain,secure){ 5 document.cookie = name + "=" + value + ";expires="+ date + ";" 6 +(path ? "path=" + path + ";" : "") 7 +(domain ? "domain=" + domain + ";" : "") 8 +(secure ? "secure=" + secure + ";" : ""); 9 return this; 10 }, 11 removeCookie:function(name){ 12 document.cookie = name + "=;max-age=-1"; 13 return this; 14 } 15 } 16 var date = new Date(); 17 date.setDate(date.getDate()+1); 18 manageCookie.setCookie("sss","SSS",date); 19 manageCookie.deleteCookie("sss");
最后还来一个cookie查找吧:
1 getCookie:function(name,callback){ 2 var arrCookies = document.cookie.split("; "); 3 for(var i = 0; i < arrCookies.length; i++){ 4 var itemCookie = arrCookies[i].split("="); 5 if(itemCookie[0] == name){ 6 callback(itemCookie[1]);//将找到的第一个匹配cookie的值传给回调函数处理 7 break; 8 } 9 } 10 return this; 11 }
四、cookie的缺陷
1.由于cookie采用文本形式存在计算机本地,从信息安全角度来讲非常的不安全,所以不能存储敏感信息。
2.cookie可以人为的在浏览器其设置禁用,所以不能保证能有效支持相关功能实现
通过cookie实现页面刷新的拖拽定位demo:
1 //css 2 #demo{ 3 position: absolute; 4 width: 100px; 5 height: 100px; 6 top: 100px; 7 left: 100px; 8 background-color: orange; 9 } 10 //html、JS 11 <div id="demo"></div> 12 <script> 13 var oDemo = document.getElementById('demo'); 14 var manageCookie = { 15 //name,alue, date(expires), domain, path, secure 16 //名称/值, 过期时间,域名, 路径, 安全 17 setCookie:function(name,value,date,path,domain,secure){ 18 document.cookie = name + "=" + value + ";expires="+ date + ";" 19 +(path ? "path=" + path + ";" : "") 20 +(domain ? "domain=" + domain + ";" : "") 21 +(secure ? "secure=" + secure + ";" : ""); 22 return this; 23 }, 24 removeCookie:function(name){ 25 document.cookie = name + "=;max-age=session"; 26 return this; 27 }, 28 getCookie:function(name,callback){ 29 var arrCookies = document.cookie.split("; "); 30 for(var i = 0; i < arrCookies.length; i++){ 31 var itemCookie = arrCookies[i].split("="); 32 if(itemCookie[0] == name){ 33 callback(itemCookie[1]);//将找到的第一个匹配cookie的值传给回调函数处理 34 break; 35 } 36 } 37 return this; 38 } 39 } 40 var drag = { 41 //入口函数--启动拖拽功能,为拖拽体绑定鼠标按下事件 42 //this指向功能管理对象 43 init:function(oDemo){ 44 this.dom = oDemo; 45 var _this = this; 46 this.bindEvent(); 47 manageCookie.getCookie("dragLeft",function(data){ 48 if(data){ 49 _this.dom.style.left = data + "px"; 50 } 51 }); 52 manageCookie.getCookie("dragTop",function(data){ 53 if(data){ 54 _this.dom.style.top = data + "px"; 55 } 56 }); 57 }, 58 //鼠标按下事件绑定--当鼠标按下时为document绑定鼠标移动事件 59 //this指向拖拽体DOM 60 bindEvent:function(){ 61 this.dom.onmousedown = this.mouseDown.bind(this); 62 }, 63 //鼠标移动、鼠标松开事件 64 //this指向功能管理对象 65 //实现鼠标按下时需要获取的数据:鼠标位置,DOM位置 66 mouseDown:function(e){ 67 document.onmousemove = this.mouseMove.bind(this); 68 document.onmouseup = this.mouseUp.bind(this); 69 //将鼠标位置保存到功能管理对象drag,方便鼠标移动时取值 70 this.disY = e.clientY - this.dom.offsetTop; 71 this.disX = e.clientX - this.dom.offsetLeft; 72 }, 73 //鼠标移动事件回调函数 74 //this指向document 75 //鼠标移动时修改DOM位置--实现物体移动 76 mouseMove:function(e){ 77 //将时时的DOM位置保存到功能管理对象上,方便鼠标抬起时写入cookie 78 this.newLeft = e.clientX - this.disX; 79 this.newTop = e.clientY - this.disY; 80 this.dom.style.left = this.newLeft + 'px'; 81 this.dom.style.top = this.newTop + 'px'; 82 }, 83 //鼠标松开事件回调函数 84 //this指向document 85 //当鼠标送时将鼠标移动事件取消--物体脱离鼠标 86 //鼠标松开事件也可以取消了。 87 //将DOM位置写入cookie 88 mouseUp:function(){ 89 document.onmousemove = null; 90 document.onmouseup = null; 91 manageCookie.setCookie("dragLeft",this.newLeft,10000); 92 manageCookie.setCookie("dragTop",this.newTop,10000); 93 } 94 } 95 drag.init(oDemo); 96 </script>