概述
目录
前言
前言
一个插件 js-cookie
cookie 与 sessionStorage、localStorage 的对比 请 参见此文 的相关小节。
Cookie 用来:存储客户端的 HTTP 状态信息。
Cookie 的特点:
- 同一个域名下的 cookie 是共享的。不利于 http 性能的提升,而且不同域名间会产生跨域。
- 同步存储。
- 数量受限,大小受限。不同的浏览器对 cookie 都有各自的数量限制,且每个 cookie 只能存储 4KB 大小的数据。
- 可以设置有效期。关闭浏览器后,没有设置有效期的 cookie 会被清掉,设置了有效期的 cookie 会继续生效,直到过期时自动清掉。
一、cookie 的结构
cookie 由浏览器保存的以下几块信息构成:
- 名称(name):唯一的 cookie 名,必须被 URL 编码(关于 URL 编码请戳:https://blog.csdn.net/mChales_Liu/article/details/106660255)。
- 值(value):储存在 cookie 中的字符串值,必须被 URL 编码。
- 域(domain):cookie 有效的域。比如:“http://www.test.com”。
- 路径(path):指定域中的某个路径可以向服务器发送 cookie,一旦指定,别的路径都不能发送 cookie 了,即使是同域名。比如:“http://www.test.com/books”。
- 失效时间(expires):表示 cookie 何时被删除的时间戳。默认情况下,浏览器会话结束时即将所有 cookie 都删除了;不过也可以自己设置删除时间,因此,cookie 可在浏览器关闭后依然保存在用户的机器上。
- samesite:主要用来预防“跨站点请求伪造攻击(CSRF)”的,可以设置的值有三个:
- strict:完全禁止第三方获取 cookie,跨站点时,任何情况下都不会发送 cookie;
- lax:Chrome 默认值。大多数情况下禁止获取 cookie,除非导航到目标网址的 GET 请求(链接、预加载、GET表单);
- none:没有限制。
- 安全标志(secure):指定后,cookie 只有在使用 SSL 链接( https 访问)时才能被发送到服务器。若 cookie 中设置了 samesite=none,必须设置 secure。
设置了 secure 和没设置 secure 的区别:
设置了 secure 之后,cookie 信息只能采用 SSL 的 https 协议发送给服务器,而 http 协议下就发送不了了。所以下面的例子中,“赵云”成功写入了 cookie 信息,而“韩信”却失败了。
document.cookie = "zyCookie=赵云";
document.cookie = "hxCookie=韩信; secure";
cookie 的每一段信息都是 Set-Cookie 头的一部分,他们使用“; ”分隔每一段信息,其中只有 cookie 的名字和值是必须的。
下面是一段带有 cookie 信息的请求头:
/* 访问 https://www.test.com 的请求头 */
HTTP/1.1 200 0K
Content-type: text/html
Set-Cookie: name=myCookie; path=/; domain=.test.com; expires=Mon, 22-Jan-27 18:10:17 GMT; secure
Other-header: other-header-value
根据上述代码,分析一下这条 cookie:
- 名称——请求头中指定了一个叫做 name 的 cookie;
- 值——值为 myCookie;
- 路径——对于由 path 指定域名下的所有页面都有效(这里“/”指的是整个域“www.test.com”(www 可省略));
- 域——域为 .test.com;
- 有效期——它会在 2027年1月22号 18:10:17 失效;
- 安全标志——这个 cookie 设置了安全标志 secure。
二、JS 操作 cookie
基本的 cookie 操作有三种:读取、写入 和 删除。
下面封装了一个 JavaScript 操作 cookie 的对象:
var CookieUtil = {
// 读取 cookie
get: function(name){
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null,
cookieEnd;
if(cookieStart > -1){
cookieEnd = document.cookie.indexOf(";", cookieStart);
if(cookieEnd == -1){
cookieEnd = document.cookie.length;
}
cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
}
return cookieValue;
},
// 写入 cookie
set: function(name, value, expires, path, domain, secure){
var cookieText = encodeURIComponent(name) + "=" + encodeURIComponent(value);
if(expires instanceof Date){
cookieText += "; expires=" + expires.toGMTString();
}
if(path){
cookieText += "; path=" + path;
}
if(domain){
cookieText += "; domain=" + domain;
}
if(secure){
cookieText += "; secure";
}
document.cookie = cookieText;
},
// 删除 cookie
unset: function(name, path, domain, secure){
this.set(name, "", new Date(0), path, domain, secure);
}
}
使用 CookieUtil 对象时注意:
- 此处,我开启了本地服务器(Wampserver)
- 写入 cookie 时,name 和 value 是必须要传的,否则会添加一个 name 或 value 值是 undefined 的 cookie。
CookieUtil.set();
CookieUtil.set("test01");
正确使用 CookieUtil 对象的栗子:
1、写入 cookie
// 写入 cookie
CookieUtil.set("test02", "two");
CookieUtil.set("test03", "three", new Date("2020.06.17 20:00:00"), "/", "localhost");
2、读取 cookie
// 读取 cookie
console.log(CookieUtil.get("test02")); // two
console.log(document.cookie); // test02=two; test03=three
3、删除 cookie
// 删除 cookie
CookieUtil.unset("test02");
CookieUtil.unset("test03");
还未到失效时间的 cookie 是删不掉的,到期后浏览器会自动删除。
三、子 cookie
子 cookie 就是使用 cookie 值来存储多个 cookie。它的作用是——绕开浏览器对单个域名下 cookie 数量的限制。
如果你担心开发中可能会达到单域名的 cookie 上限,那么子 cookie 可能是一个非常有吸引力的备选方案。不过,你需要更加密切地关注 cookie 的长度,以防超过单个 cookie 的长度限制。
子 cookie 与 cookie 的常见格式对比:
// 子 cookie
name=name1=value1&name2=value2&name3=value3
// cookie
name=value
下面封装了读取、写入和删除子 cookie 的代码:
var SubCookieUtil = {
/**
* 获取子 cookie
*/
get: function(name, subName){
var subCookies = this.getAll(name);
if(subCookies){
return subCookies[subName];
}else{
return null;
}
},
getAll: function(name){
var cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;
cookieEnd = "",
subCookies = "",
i = 0,
parts = [],
result = {};
if(cookieStart > -1){
cookieEnd = document.cookie.indexOf(";", cookieStart);
if(cookieEnd == -1){
cookieEnd = document.cookie.length;
}
cookieValue = document.cookie.substring(cookieStart + cookieName.length, cookieEnd);
if(cookieValue.length > 0){
subCookies = cookieValue.split("&");
for(i, len=subCookies.length; i<len; i++){
parts = subCookies[i].split("=");
result[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
}
return result;
}
}
return null;
},
/**
* 设置(写入)子 cookie
*/
set: function(name, subName, value, expires, path, domain, secure){
var subcookies = this.getAll(name) || {};
subcookies[subName] = value;
this.setAll(name, subcookies, expires, path, domain, secure);
},
setAll: function(name, subcookies, expires, path, domain, secure){
var cookieText = encodeURIComponent(name) + "=",
subcookieParts = new Array();
for(var subName in subcookies){
if(subName.length > 0 && subcookies.hasOwnProperty(subName)){
subcookieParts.push(encodeURIComponent(subName) + "=" + encodeURIComponent(subcookies[subName]));
}
}
if(subcookieParts.length > 0){
cookieText += subcookieParts.join("&");
if(expires instanceof Date){
cookieText += "; Expires=" + expires.toGMTString();
}
if(path){
cookieText += "; path=" + path;
}
if(domain){
cookieText += "; Domain=" + domain;
}
if(secure){
cookieText += "; Secure";
}
}else{
cookieText += "; expires=" + (new Date(0)).toGMTString();
}
document.cookie = cookieText;
},
/**
* 删除子 cookie
*/
unset: function(name, subName, path, domain, secure){
var subcookies = this.getAll(name);
if(subcookies){
delete subcookies[subName];
this.setAll(name, subcookies, null, path, domain, secure);
}
},
unsetAll: function(name, path, domain, secure){
this.setAll(name, null, new Date(0), path, domain, secure);
}
}
由上述代码可知:
当获取子 cookie 时,用 get() 方法获取单个 cookie 的值,用 getAll() 方法获取所有子 cookie 并将它们放入一个对象中返回,对象的属性为子 cookie 的名称,对应值为子 cookie 对应的值。其实,get() 方法就是调用 getAll() 获取所有的子 cookie,然后只返回所需要的那一个(如果 cookie 不存在,就返回 null)。getAll() 方法和 get() 方法在解析 cookie 值的方式上非常相似。区别在于 cookie 的值并非立即解码,而是先根据 & 字符将子 cookie 分割出来放在一个数组里,每一个子 cookie 再根据等于号分割,这样在 parts 数组中的前一部分便是子 cookie 名,后一部分便是子 cookie 值。这两项都要使用 encodeURIComponent() 方法来解码,然后放入 result 对象中,最后最为方法的返回值。如果 cookie 不存在,则返回 null。
当设置(写入)子 cookie 时,用 set() 方法获取指定 cookie 名称的所有子 cookie。当 getAll() 返回 null 时,用逻辑或操作符“||” 将 subcookies 设置为一个新对象。然后,在 subcookies 对象上设置好子 cookie 值并传给 setAll() 方法。然后,setAll() 方法使用 for-in 循环遍历第二个参数中的属性。然后,使用 hasOwnProperty() 方法确保只有实例属性被序列化到子 cookie 中。由于可能会存在属性名为空字符串的情况,所以把属性名加入结果对象之前还要检查一下属性名的长度。将每个子 cookie 的名值对儿都存入 subcookieParts 数组中,以便稍后可以使用 join() 方法以 & 号组合起来。剩下的步骤就和 CookieUtil.set() 一样了。
当删除子 cookie 时,普通的 cookie 可以通过将失效时间设置为过去的时间的方法来删除,但是子 cookie 不能这样做。为了删除一个子 cookie,首先必须获取包含在某个 cookie 中的所有子 cookie,然后,仅删除需要删除的那个子 cookie,然后再将余下的子 cookie 的值保存为 cookie 的值。基于此,用 unset() 方法删除某个 cookie 中的单个子 cookie 而不影响其他的 cookie;而 unsetAll() 方法等同于 CookieUtil.unset() 方法,用于删除整个 cookie。
1、写入子 cookie
// 逐个设置子 cookie
SubCookieUtil.set("data01", "heroname", "Sunwukong");
SubCookieUtil.set("data01", "heroAttribute", "assassin warrior");
// 一次设置多个子 cookie
SubCookieUtil.setAll("data02", {heroname:"Machao", heroAttribute:"assassin"}, new Date("2020.06.18 17:00:00"));
2、获取子 cookie
// 获取某个 cookie 里的单一子 cookie
console.log(SubCookieUtil.get("data02", "heroname")); // Machao
console.log(SubCookieUtil.get("data02", "heroAttribute")); // assassin
// 获取某个 cookie 里的全部子 cookie
console.log(SubCookieUtil.getAll("data01")); // {heroname: "Sunwukong", heroAttribute: "assassin warrior"}
3、删除子 cookie
// 逐个删除子 cookie
SubCookieUtil.unset("data01", "heroAttribute");
// 删除全部子 cookie
SubCookieUtil.unsetAll("data02");
四、cookie 的局限性
cookie 用来存储大小在 4KB 以内的小数据。当 cookie 的大小超过最大限制时,该 cookie 会被浏览器默默地丢弃掉。
不同浏览器对 cookie 的个数要求不同:
- IE6 及其以下版本限制每个域最多 20 个 cookie。
- IE7 及其以上版本和 Firefox 限制每个域最多 50 个 cookie。
- Opera 限制每个域最多 30 个 cookie。
- Safari 和 Chrome 对每个域的 cookie 数量没有限制。
cookie 在性质上是绑定在特定的域名之下的。当设定了一个 cookie 后,再给创建它的域名发送请求时,都会包含这个 cookie。这个限制确保了储存在 cookie 中的信息只能让批准接受者(同源者)访问,而无法被其他域访问(不能跨域)。
五、解决 cookie 的跨域
在写入 cookie 时遇到 samesite 跨域问题。
原因:当 cookie 中没有设置 samesite 属性时,Chrome80 默认设置了 samesite=lax。
解决方案:
第一步:后端设置 samesite=None
用 Ajax 请求时,在后端这样设置:
response.setHeader(“Set-Cookie”, “HttpOnly;Secure;SameSite=None”);
设置 samesite=none 的同时必须设置 secure,不设置 secure 无效,而设置 secure 之后这样设置后就只能采用“https”与服务器通信了。
第二步:禁用 Chrome 的 SameSite 功能
samesite 的设计初衷是为了防止 CSRF 攻击,禁用 samesite 实际上并没有解决问题,属于下下策。
进入这个网站:chrome://flags/
然后,找到 SameSite,将找到的三个功能均设置为“Disabled”,最后点击 reLaunch 按钮重启 Chrome。
有更好的方案欢迎大家留言。
六、cookie 常见的问题
1、关闭浏览器后 cookie 会消失吗?
看情况:
- 若此时 cookie 没有持久化,浏览关闭后 cookie 会消失;
- 若此时 cookie 进行了持久化,浏览器关闭后 cookie 不会消失。
【推荐阅读】
cookie、token 和 session
最后
以上就是高贵大叔为你收集整理的Cookie 的使用前言一、cookie 的结构二、JS 操作 cookie三、子 cookie四、cookie 的局限性五、解决 cookie 的跨域六、cookie 常见的问题的全部内容,希望文章能够帮你解决Cookie 的使用前言一、cookie 的结构二、JS 操作 cookie三、子 cookie四、cookie 的局限性五、解决 cookie 的跨域六、cookie 常见的问题所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复