前记:前段时间搞一个活动,开发的时间被严重压缩,忙到飞起,以致于都没怎么写文章了,内疚.
2 月份参加了一场面试,有一些关于 cookie 的问题回答的不是很好,所以这篇文章我们来对 cooKie 做一个探讨和总结,查漏补缺。其实本文很早之前都写的差不多了,不过关于 cookie 跨域方面,查了比较多的资料,始终没有一个太好的结果,所以本文一直没有发布。
本文的很多内容都是参考网上的资料,可以说是好几篇资料的集合,毕竟是总结嘛,就是将自己觉得有用的东西集合在一起。
官方定义:Netscape 官方文档中的定义为,Cookie 是指在 HTTP 协议下,服务器或脚本可以维护客户端计算机上信息的一种方式 。通俗地说,Cookie 是一种能够让网站 web 服务器把少量数据储存到客户端的硬盘或内存里,或是从客户端的硬盘里读取数据的一种技术。 Cookie 文件则是指在浏览某个网站时,由 Web 服务器的 CGI 脚本创建的存储在浏览器客户端计算机上的一个小文本文件,其格式为:用户名 @网站地址 [数字].txt。
再通俗一点的讲,由于 HTTP 是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。
HTTP 协议是一种无状态、无连接的协议,不能在服务器上保持一次会话的连续状态信息。Cookie 的作用是记录用户的有关信息,它最根本的用途是帮助 Web 站点保存有关访问者的信息。如身份识别号码 ID、密码、浏览过的网页、停留的时间、用户在 Web 站点购物的方式或用户访问该站点的次数等,当用户再次链接 Web 服务器时,浏览器读取 Cookie 信息并传递给 Web 站点。
我们先来看一张图:
在谷歌浏览器开发者模式中,我们可以看到网站的 cookie,所以,相应的,我们就可以知道 cookie 的一些属性了,接下来介绍 Cookie 中的一些属性
如图所示,cookie 具有的属性有 Name、value、Domain、path、Expires/Max-Age、Size、HTTP、Secure 等等,我们接下来详细了解了解
Name:
该 Cookie 的名称,一旦创建,名称便不可更改
value:
该 Cookie 的值,如果值为 Unicode 字符,需要为字符编码, 如果值为二进制数据,则需要使用 BASE64 编码
domain:
可以访问该 Cookie 的域名。如果设置为 ".google.com", 则所有以 "google.com" 结尾的域名都可以访问该 Cookie。注意第一个字符必须为 "."
这个 domain 稍作解释:
非顶级域名,如二级域名或者三级域名,设置的 cookie 的 domain 只能为顶级域名或者二级域名或者三级域名本身,不能设置其他二级域名的 cookie,否则 cookie 无法生成。
顶级域名只能设置 domain 为顶级域名,不能设置为二级域名或者三级域名,否则 cookie 无法生成。
二级域名能读取设置了 domain 为顶级域名或者自身的 cookie,不能读取其他二级域名 domain 的 cookie。所以要想 cookie 在多个二级域名中共享,需要设置 domain 为顶级域名,这样就可以在所有二级域名里面或者到这个 cookie 的值了。
顶级域名只能获取到 domain 设置为顶级域名的 cookie,其他 domain 设置为二级域名的无法获取。
Path:
path 字段为可以访问此 cookie 的页面路径。 比如 domain 是 abc.com, path 是 / detail,那么只有 / detail 路径下的页面可以读取此 cookie。
Expires/Max-Age:
该 Cookie 失效时间,单位秒。如果为正数,则 Cookie 在 maxAge 秒之后失效。
如果为负数,该 Cookie 为临时 Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存 Cookie.
如果为 0,表示删除 Cookie。默认是 - 1
Size:
cookie 的大小
http:
cookie 的 httponly 属性。若此属性为 true,则只有在 http 请求头中会带有此 cookie 的信息,而不能通过 document.cookie 来访问此 cookie。
比如截图中的__jsluid
secure:
设置是否只能通过 https 来传递此条 cookie
1、一个浏览器针对一个网站最多存 20 个 Cookie,浏览器一般只允许存放 300 个 Cookie
2、每个 Cookie 的长度不能超过 4KB(稀缺)。但不同的浏览器实现的不同
3、Cookie 的不可跨域名性。
例如:Cookie 在客户端是由浏览器来管理的,浏览器能够保证 Google 只会操作 Google 的 Cookie 而不会操作 Baidu 的 Cookie,从而保证用户的隐私安全。
cookie 有两种类型:
不设置过期时间,则表示这个 cookie 生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie 就消失了。这种生命期为浏览会话期的 cookie 被称为会话 cookie。会话 cookie 一般不保存在硬盘上而是保存在内存里。可以类比于本地存储的 sessionstore
设置了过期时间,浏览器就会把 cookie 保存到硬盘上,关闭后再次打开浏览器,这些 cookie 依然有效直到超过设定的过期时间。
存储在硬盘上的 cookie 可以在不同的浏览器进程间共享,比如两个 IE 窗口。而对于保存在内存的 cookie,不同的浏览器有不同的处理方式。可以类比于本地存储的 localstore
服务器端像客户端发送 Cookie 是通过 HTTP 响应报文实现的,在 Set-Cookie 中设置需要像客户端发送的 cookie,cookie 格式如下:
Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"
其中 name=value 是必选项,其它都是可选项。
客户端的话用 js 即可操作,由于现在客户端设置大部分用 H5 的本地存储 localstore 和 sessionstore 多一点,所以客户端的这里不做介绍
这里介绍的 js 来读取 cookie,可以直接使用下面的方法,其实就是用 document.cookie:
- function getCookie(name){
- var cookieName=encodeURIComponent(name)+"=",
- cookieStart=document.cookie.indexOf(cookieName),
- cookieValue=null;
- if(cookieStart>-1){
- var cookieEnd=document.cookie.indexOf(";",cookieStart);
- if(cookieEnd==-1){
- cookieEnd=document.cookie.Length;
- }
- cookieValue=decodeURIComponent(document.cookie.substring(cookieStart+document.cookie.length,cookieEnd));
- }
- return cookieValue;
- }
Cookie 并不提供修改、删除操作。如果要修改某个 Cookie,只需要新建一个同名的 Cookie,添加到 response 中覆盖原来的 Cookie。
如果要删除某个 Cookie,只需要新建一个同名的 Cookie,并将 maxAge 设置为 0,并添加到 response 中覆盖原来的 Cookie。注意是 0 而不是负数。
Cookie 实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。如下图所示:
这个跟其实跟浏览器你器缓存有点类似,具体的过程我们可以分解分解:
(1)客户端在浏览器的地址栏中键入 Web 服务器的 URL,浏览器发送读取网页的请求。
(2)服务器接收到请求后,产生一个 Set-Cookie 报头,放在 HTTP 报文中一起回传客户端,发起一次会话。
(3)客户端收到应答后,若要继续该次会话,则将 Set-Cook-ie 中的内容取出,形成一个 Cookie.txt 文件储存在客户端计算机里。
(4)当客户端再次向服务器发出请求时,浏览器先在电脑里寻找对应该网站的 Cookie.txt 文件。如果找到,则根据此 Cookie.txt 产生 Cookie 报头,放在 HTTP 请求报文中发给服务器。
(5)服务器接收到包含 Cookie 报头的请求,检索其 Cookie 中与用户有关的信息,生成一个客户端所请示的页面应答传递给客户端。 浏览器的每一次网页请求,都可以传递已存在的 Cookie 文件,例如,浏览器的打开或刷新网页操作。
通常 cookie 信息都是使用 http 连接传递数据,这种传递方式很容易被查看,而且 js 里面直接有一个 document.cookie 方法,可以直接获取到用户的 cooie, 所以 cookie 存储的信息容易被窃取。假如 cookie 中所传递的内容比较重要,那么就要求使用加密的数据传输。
如何来防范 cookie 的安全呢?有以下几种方法:
(1)HttpOnly 属性
如果在 Cookie 中设置了 "HttpOnly" 属性,那么通过程序 (JS 脚本、Applet 等) 将无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。
(2)secure 属性
当设置为 true 时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被盗取到 Cookie 的具体内容。
我们再来看看一道经典的面试题:
登录时候用 cookie 的话,安全性问题怎么解决?
这个问题,网上找了比较久的答案,比较满意的有两种答案(答案是网上找的)
第一种是:
把用户对象(包含了用户 ID、用户名、是否登录..)序列化成字符串再加密存入 Cookie。
密钥是:客户端 IP + 浏览器 Agent + 用户标识 + 固定的私有密钥
当 cookie 被窃取后,只要任一信息不匹配,就无法解密 cookie,进而也就不能登录了。
这样做的缺点是 IP 不能变动、频繁加密解密会加重 CPU 负担
第二种是:
将用户的认证信息保存在一个 cookie 中,具体如下:
1.cookie 名:uid。推荐进行加密,比如 MD5('站点名称') 等。
2.cookie 值:登录名 | 有效时间 Expires|hash 值。hash 值可以由 "登录名 + 有效时间 Expires + 用户密码(加密后的)的前几位 +salt" (salt 是保证在服务器端站点配置文件中的随机数)
这样子设计有以下几个优点:
1. 即使被盗了,盗用者还是无法登录到系统,因为组成 cookie 值的 salt 是保证在服务器站点配置文件中而非数据 库。
2. 如果账户被盗了,用户修改密码,可以使盗用者的 cookie 值无效。
3. 如果服务器端的数据库被盗了,通过修改 salt 值可以使所有用户的 cookie 值无效,迫使用户重新登录系统。
4. 有效时间 Expires 可以设置为当前时间 + 过去时间(比如 2 天),这样可以保证每次登录的 cookie 值都不一样,防止盗用者 窥探到自己的 cookie 值后作为后门,长期登录。
cookie 是不能跨域访问的,那么,假如需要跨域来进行 cookie 的访问和传递,该怎么办呢?查找了比较多的资料,比较少这方面的资料,
在 cookie 跨域这个问题上,前端能做的不多,很多都是需要和后端一起配合来完成。
总结了下面的几种方法,具体的实现过程这里没有写,可以点击我提供的链接自己看看。
前 2 种具体的实现方法可以点击看这里:
1、nginx 方向代理:
反向代理(Reverse Proxy)方式是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
反向代理服务器对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理 的命名空间 (name-space) 中的内容发送普通请求,接着反向代理将判断向何处 (原始服务器) 转交请求,并将获得的内容返回给客户端,就像这些内容 原本就是它自己的一样。
2、jsonp 方法:
这个方法和我们平时处理 js 跨域的 jsonp 方法一样。具体实现方法可以看看淘宝的解决方法,
3、nodejs 的 superagent
4、iframe 方法:
比如有个 www.a.com/index.html 的页面,往 www.b.com/index.html 的页面传递 cookie
www.a.com/index.html 这样写:
- document.cookie = "name=" + "value;" + "expires=" + "datatime;" + "domain=" + "" + "path=" + "/path" + "; secure";
- //name Cookie名字
- //value Cookie值
- //expires 有效期截至(单位毫秒)
- //path 子目录
- //domain 有效域
- //secure 是否安全
- window.location = "http://www.b.com/index.html?" + document.cookie; //跳转到b页面
www.b.com/index.html 这样写:
- var url = window.location.toString();//获取地址
- var get = url.substring(url.indexOf("abc"));//获取变量和变量值
- var idx = get.indexOf("=");//获取变量名长度
- if (idx != -1) {
- var name = get.substring(0, idx);//获取变量名
- var val = get.substring(idx + 1);//获取变量值
- setCookie(name, val, 1);//创建Cookie
- }
备注:本文主要是查找了网上比较多的资料来总结 cookie 的一些知识,文笔有限,有误之处,欢迎指出
来源: http://www.cnblogs.com/xianyulaodi/p/6476991.html