啥是cookie?
cookie是一小段文本信息,伴随着用户请求在web服务器和浏览器之间传递
用于解决 "如何记录客户端的用户信息"
cookie存在哪里?
cookie 是存在用户硬盘中,用户每次访问站点时,Web应用程序都可以读取 Cookie 包含的信息。当用户再次访问这个站点时,浏览器就会在本地硬盘上查找与该 URL 相关联的 Cookie。如果该 Cookie 存在,浏览器就将它添加到request header的Cookie字段中,与http请求一起发送到该站点。
cookie如何添加?
- 客户端设置:js提供api -- document.cookie
⚠️注意:document.cookiecookie的名和值中不能出现分号、逗号、等号和空格,每一个key之间通过分号和空格来分割。
document.cookie = 'name=value; maxAge=3000; path=/; domain=xx.com; secure'
设置多个cookie
通过document.cookie的方式设置cookie每次只能设置一个,写多个会如何呢?
document.cookie = 'cookie1=value1; cookie2=value2'
尝试一下,会发现只有第一个cookie1设置成功,cookie2被无视,因此设置多个cookie,最简单就是多次调用document.cookie
- 服务端设置:http响应头
浏览器发出的有些请求返回头中有Set-Cookie的字段,服务器通过这个字段告知浏览器它需要设置的cookie,然后浏览器检查一下设置的内容是否符合浏览器对cookie的要求,符合条件则设置成功
⚠️注意:响应头里set-cookie字段可以有多个,每一个对应一个要设置的cookie,且只能对应一个。
cookie如何获取?
- 客户端主动获取
同样是js提供的api -- document.cookie
var cookies = document.cookie
- 请求头中携带
浏览器发送请求时,在请求的头中会自动将“符合条件”的cookie带上。
既然是请求头中携带的,那么我们通过ajax发送请求的时候,能否顺便设置一下cookie呢?
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.withCredentials = true;
xhr.setRequestHeader('Cookie', "key=value");
xhr.send(null);
xhr.withCredentials:
一般跨域请求的时候,比如A域接收B域的请求时,接收不到B域的cookies,也就是说,A服务器的request.getCookies() 为空。解决这个问题,一般把XMLHttpRequest这个对象的withCredentials属性设置为true
实验一下可以发现,我们设置的cookie并没有生效,并且chrome浏览器下我们会看到一行报错
Refused to set unsafe header "Cookie"
因为浏览器的安全限制,我们不能自己随便设置请求头中的cookie。
cookie有哪些属性?
属性名 | 说明 |
---|---|
domain | 限制cookie的使用范围,只能在domain值范围内才能访问该cookie |
path | domain和path共同限制了课访问该cookie的url,如果某个cookie限制了path='/abc',那么只有'domain/abc'下的所有url可以访问该cookie |
expires/max-age | cookie的过期时间,默认设置为session,即页面关闭后cookie会被清理。 expires 是 http/1.0协议中的选项,在新的http/1.1协议中expires已经由 max-age 选项代替。expires必须是 GMT 格式的时间。 max-age的单位为秒,cookie失效时刻 = 创建时刻 + max-age |
httpOnly | 如果被标记为httpOnly,那么document.cookie的方式就无法获取到该cookie。同样的,我们通过js来设置cookie的,也无法被标记为httpOnly。 这个值只能通过请求的响应头来设置。默认情况下,cookie不会带httpOnly选项。 |
secure | 对于被标记为Secure的cookie,只有当请求是HTTPS或者其他安全协议时,该cookie 才能被访问到。 同样的,也只有在HTTPS或者其他安全协议时,我们也才能通过js设置secure的cookie。 |
sameSite | 谷歌开发的一种安全机制,该属性表示 Cookie 不随着跨域请求发送,其目的是尝试阻止CSRF。 |
对于domain值的要求
- 自身:domain的值可以设置为本身。
- 向下:所有子域名
如果当前页面的域名是 sub.test.com, 那么 domain 可以设置为.sub.test.com。允许所有sub.test.com的子域名访问,如xx.sub.test.com、xx.xx.sub.test.com。
- 向上:父级域名,直到顶级域名的下一级
如果当前页面的域名是sub.test.com, 那么domain可以设置为test.com。但是不能设置为.com。因为.com属于Top-Level Domain。
cookie的缺点?
- 安全性弱:由于cookie在http中是明文传递的,其中包含的数据都可以被他人访问,可能会被篡改、盗用。
- 大小限制:cookie的大小限制在4kb左右,不适合大量存储。
- 增加流量:cookie每次请求都会被自动添加到Request Header中,无形中增加了流量。cookie信息越大,对服务器请求的时间越长。
cookie插件
tiny-cookie(vue-cookie)
/*!
* tiny-cookie - A tiny cookie manipulation plugin
* https://github.com/Alex1990/tiny-cookie
* Under the MIT license | (c) Alex Chao
*/
!(function(root, factory) {
// Uses CommonJS, AMD or browser global to create a jQuery plugin.
// See: https://github.com/umdjs/umd
if (typeof define === 'function' && define.amd) {
// Expose this plugin as an AMD module. Register an anonymous module.
define(factory);
} else if (typeof exports === 'object') {
// Node/CommonJS module
module.exports = factory();
} else {
// Browser globals
root.Cookie = factory();
}
}(this, function() {
'use strict';
// The public function which can get/set/remove cookie.
function Cookie(key, value, opts) {
if (value === void 0) {
return Cookie.get(key);
} else if (value === null) {
Cookie.remove(key);
} else {
Cookie.set(key, value, opts);
}
}
// 检查是否启用了cookie
Cookie.enabled = function() {
var key = '__test_key';
var enabled;
document.cookie = key + '=1';
enabled = !!document.cookie;
if (enabled) Cookie.remove(key);
return enabled;
};
// 根据key名得到cookie的值
Cookie.get = function(key, raw) {
if (typeof key !== 'string' || !key) return null;
key = '(?:^|; )' + escapeRe(key) + '(?:=([^;]*?))?(?:;|$)';
var reKey = new RegExp(key);
var res = reKey.exec(document.cookie);
return res !== null ? (raw ? res[1] : decodeURIComponent(res[1])) : null;
};
// 在不解码的情况下获取cookie的值。
Cookie.getRaw = function(key) {
return Cookie.get(key, true);
};
// 设置cookie的值
Cookie.set = function(key, value, raw, opts) {
if (raw !== true) {
opts = raw;
raw = false;
}
opts = opts ? convert(opts) : convert({});
var cookie = key + '=' + (raw ? value : encodeURIComponent(value)) + opts;
document.cookie = cookie;
};
// 在不编码的情况下设置cookie的值
Cookie.setRaw = function(key, value, opts) {
Cookie.set(key, value, true, opts);
};
// 用指定的键删除cookie
Cookie.remove = function(key) {
Cookie.set(key, 'a', { expires: new Date() });
};
// 工具函数
// ---------------
// 转义特殊字符
function escapeRe(str) {
return str.replace(/[.*+?^$|[\](){}\\-]/g, '\\$&');
}
// 将对象转换为cookie选项字符串。
function convert(opts) {
var res = '';
for (var p in opts) {
if (opts.hasOwnProperty(p)) {
if (p === 'expires') {
var expires = opts[p];
if (typeof expires !== 'object') {
expires += typeof expires === 'number' ? 'D' : '';
expires = computeExpires(expires);
}
opts[p] = expires.toUTCString(); // 可根据世界时 (UTC) 把 Date 对象转换为字符串
}
if (p === 'secure') {
if (opts[p]) {
res += ';' + p;
}
continue;
}
res += ';' + p + '=' + opts[p];
}
}
if (!opts.hasOwnProperty('path')) {
res += ';path=/';
}
return res;
}
// 按给定字符串返回expires
function computeExpires(str) {
var expires = new Date();
var lastCh = str.charAt(str.length - 1);
var value = parseInt(str, 10);
switch (lastCh) {
case 'Y': expires.setFullYear(expires.getFullYear() + value); break;
case 'M': expires.setMonth(expires.getMonth() + value); break;
case 'D': expires.setDate(expires.getDate() + value); break;
case 'h': expires.setHours(expires.getHours() + value); break;
case 'm': expires.setMinutes(expires.getMinutes() + value); break;
case 's': expires.setSeconds(expires.getSeconds() + value); break;
default: expires = new Date(str);
}
return expires;
}
return Cookie;
}));