Fork me on GitHub

认清cookie!

啥是cookie?

cookie是一小段文本信息,伴随着用户请求在web服务器和浏览器之间传递
用于解决 “如何记录客户端的用户信息”

cookie存在哪里?

cookie 是存在用户硬盘中,用户每次访问站点时,Web应用程序都可以读取 Cookie 包含的信息。当用户再次访问这个站点时,浏览器就会在本地硬盘上查找与该 URL 相关联的 Cookie。如果该 Cookie 存在,浏览器就将它添加到request header的Cookie字段中,与http请求一起发送到该站点。

cookie如何添加?

  • 客户端设置:js提供api – document.cookie
    ⚠️注意:document.cookiecookie的名和值中不能出现分号、逗号、等号和空格,每一个key之间通过分号和空格来分割。
1
document.cookie = 'name=value; maxAge=3000; path=/; domain=xx.com; secure'

设置多个cookie
通过document.cookie的方式设置cookie每次只能设置一个,写多个会如何呢?

1
document.cookie = 'cookie1=value1; cookie2=value2'

尝试一下,会发现只有第一个cookie1设置成功,cookie2被无视,因此设置多个cookie,最简单就是多次调用document.cookie

  • 服务端设置:http响应头
    浏览器发出的有些请求返回头中有Set-Cookie的字段,服务器通过这个字段告知浏览器它需要设置的cookie,然后浏览器检查一下设置的内容是否符合浏览器对cookie的要求,符合条件则设置成功
    ⚠️注意:响应头里set-cookie字段可以有多个,每一个对应一个要设置的cookie,且只能对应一个。

示例

cookie如何获取?

  • 客户端主动获取
    同样是js提供的api – document.cookie
1
var cookies = document.cookie
  • 请求头中携带
    浏览器发送请求时,在请求的头中会自动将“符合条件”的cookie带上。
    既然是请求头中携带的,那么我们通过ajax发送请求的时候,能否顺便设置一下cookie呢?
1
2
3
4
5
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*!
* 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;

}));

本文标题:认清cookie!

文章作者:tongtong

发布时间:2019年03月13日 - 21:03

最后更新:2019年03月15日 - 15:03

原始链接:https://ilove-coding.github.io/2019/03/13/认清cookie!/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束-------------