一、了解 Cookie
Cookie
最开始被设计出来是为了弥补HTTP
在状态管理上的不足。HTTP
协议是一个无状态协议,客户端向服务器发请求,服务器返回响应,故事就这样结束了,但是下次发请求如何让服务端知道客户端是谁呢?这种背景下,就产生了Cookie。
- cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。因此,服务端脚本就可以读、写存储在客户端的cookie的值。
- cookie 是不可跨站的: 每个 cookie 都绑定在特定的域名下(绑定域名下的子域都是有效的),无法在别的域名下获取使用,同域名不同端口也允许共享。
可以在浏览器控制台的 Application 面板查看Cookie:
Cookie 的数据流转
- 在首次访问网站时,浏览器发送请求中并未携带Cookie。
- 浏览器看到请求中未携带Cookie,在HTTP的响应头中加入Set-Cookie。
- 浏览器收到Set-Cookie后,会将Cookie保存下来
- 下次再访问该网站时,HTTP请求头就会携带Cookie。


Cookie都是name=value的结构,具体格式如下:
Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"
二、检测cookie是否启用
有些用户为了避免隐私泄露会在它们的浏览器中禁用cookie。因此,在js代码使用cookie前,首先要确保cookie是启用的。可以用navigator.cookieEnabled属性来判断,如果值为true,则当前cookie是启用的;反之则是禁用的(但是,只具备“当前浏览器会话生命周期”的非持久化cookie仍然是启用的)。
三、cookie属性:有效期和作用域
cookie默认的有效期很短暂,它只能维持在Web浏览器的会话期间,一旦用户关闭浏览器,cookie保存的数据就丢失了。要注意的是,这与sessionStorage的有效期还是有区别的:cookie的作用域不是局限在浏览器的单个窗口中,它的有效期和整个浏览器进程而不是单个浏览器窗口的有效期一致。如果想要延长cookie的有效期,可以通过设置max-age属性。一旦设置了有效期,浏览器就会将cookie数据存储在一个文件中,并且直到过了指定的有效期才会删除该文件。
cookie的作用域是通过文档源和文档路径来确定的。该作用域通过cookie的path和domain属性可配置。
四、Cookie常见属性
属性 | 说明 |
---|---|
name=value | 键值对,设置 Cookie 的名称及相对应的值,都必须是字符串类型(name 不区分大小写) - 如果值为 Unicode 字符,需要为字符编码。 - 如果值为二进制数据,则需要使用 base64 编码。 |
domain | Cookie生效的域名,即Cookie在哪个网站生效。默认当前访问域名。 例如我们在a.jzplp.com下设置的Cookie,就只在这个域名下生效。但是如果我们设置了domain=jzplp.com,则该Cookie可以在jzplp.com下的任何域名内生效。比如:jzplp.com,a.jzplp.com, b.jzplp.com。 domain 只能设置为当前服务器的域。 |
path | 有时候,我们希望Cookie仅仅在部分路径下生效,就可以使用Path进行限制。这里的路径就是网站的路由。默认的path=/,即在所有路径下生效。 如果设置了path=/abc,则只在/abc路径下生效。比如:
|
expires | 过期时间(GMT时间格式),当浏览器端本地的当前时间超过这个时间时,Cookie便会失效。 如果客户端和服务器时间不一致,使用expires就会存在偏差。 Expires格式:Expires=Wed, 21 Oct 2015 07:28:00 GMT。 |
Max-age | cookie 存活时间,单位秒。如果为正数,则该 cookie 在 maxAge 秒后失效。如果为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie 。如果为 0,表示删除该 cookie 。默认为 -1。 |
HttpOnly | 如果给某个 cookie 设置了 httpOnly 属性,则无法通过js读写该 cookie 的信息,但还是能通过 Application 中手动修改 cookie,所以只是在一定程度上可以防止 CSRF 攻击,不是绝对的安全 |
secure | 该 cookie 是否仅被使用安全协议传输。安全协议有 HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。 |
SameSite | 是否允许跨站请求时发送Cookie |
Priority | 当Cookie的数量超过限制时,路蓝旗会清除一部分Cookie。清除哪些合适呢?Priority属性用来定义Cookie的优先级,低优先级的Cookie会优先被清除。 Priority属性有三种: Low, Medium, High。 |
cookie集合中的每个cookie都拥有这些属性,而且每个cookie的这些属性都是独立分开的,各自控制各自的cookie。
五、cookie的局限性
1. 每个域名下cookie个数限制
- Chrome和Safari没有做硬性限制
- Firefox最多50个cookie
- IE7和之后的版本最后可以有50个cookie
- IE6或更低版本最多20个cookie
RFC 2965标准不允许浏览器保存超过300个cookie,为每个Web服务器保存的cookie数不能超过20个(是对整个服务器而言,而不仅仅指服务器上的页面和站点),而且,每个cookie保存的数据不能超过4KB。实际上,现代浏览器允许cookie总数超过300个,但是部分浏览器对单个cookie大小仍然有4KB的限制。
六、客户端对Cookie的存取
1. 读取cookie:
可以用 document.cookie 获取当前页面可用的cookie集合,其返回的值是一个字符串,该字符串都是由一系列键/值对组成,不同键/值对之间通过“分号和空格”分开。例如:
document.cookie;
// "name1=value1; name2=value2"
这些返回的cookie值并不包含键/值以外的其他cookie属性。
2. 设置cookie:
document.cookie = `name=${encodeURIComponent(name)}; max-age=1000;`;
name这个cookie会被添加到现有的cookie集合中。
由于cookie的键/值中的值是不允许包含分号、逗号和空白符,因此,在存储前一般可以采用 encodeURIComponent() 函数对值进行编码。相应的,读取cookie值的时候要用 decodeURIComponent() 函数解码。
3. 更新cookie
要改变cookie的值,需要使用相同的名字、路径和域,但是新的值重新设置cookie的值。同样地,设置新 max-age 属性就可以改变原来的cookie的有效期。
4. 删除cookie
要删除一个cookie,需要使用相同的名字、路径和域,然后指定一个任意(非空)的值,并且将 max-age 属性指定为0,再次设置cookie。
七、封装对Cookie的操作
由于cookie的读写非常不方便,我们可以自己封装一些函数来处理cookie。
1. 获取全部cookie,返回一个对象
function getAllCookies() {
let cookies = {};
const all = document.cookie;
if (all) {
const list = all.split('; ');
list.forEach(cookie => {
const cookieArr = cookie.split('=');
const name = cookieArr[0];
cookies[name] = decodeURIComponent(cookieArr[1]);
});
}
return cookies;
}
2. 获取单个cookie,设置、删除cookie
class cookieUtils {
get(name) {
var arr,
reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if ((arr = document.cookie.match(reg))) return decodeURIComponent(arr[2]);
else return null;
}
set(name, value, daysToLive) {
let cookie = `${name}=${encodeURIComponent(value)}`;
// daysToLive指天数
if (typeof daysToLive === 'number') {
cookie += `; max-age=${daysToLive * 24 * 60 * 60}`;
}
document.cookie = cookie;
}
delete(name) {
var date = new Date();
date.setTime(date.getTime() - 10000);
document.cookie = name + "=-1;expires=" + date.toGMTString();
}
}
export default new cookieUtils();
在Chrome控制台Application 的 Cookies 里可以对 cookie 进行读写操作。
移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token。
八、服务器端设置cookie示例(Node)
var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
res.setHeader('status', '200 OK');
res.setHeader('Set-Cookie', 'isVisit=true;domain=.yourdomain.com;path=/;max-age=1000');
res.write('Hello World');
res.end();
}).listen(8888);
console.log('running localhost:8888')
参考
- 浏览器中Cookie的全面介绍