在前端脚本中,为了更好的安全性,开发者对cookie的可控性是一般的,一般而言只能通过document.cookie这个变量去设置/获取cookie。
document.cookie不是一个变量上的对象,而更像一个getter和setter,也就是说获取和设置cookie的逻辑并不是单纯的设置一个字符串。
获取cookie
当通过document.cookie来获取cookie时,拿到的是所有当前 domain+path+非httpOnly 的所有cookie键值对。
cookie的可得性
cookie的可得性主要与下面两个参数有关,这两个参数是设置时就定义好的,需要注意的是js只能拿到cookie的键值对信息,如domain,path等是隐藏的,只用于浏览器自身进行判断。
domain:当前域名及父域名的cookie,假如当前域名是www.abc.com ,那么能获取到domain为www.abc.com 或者 .abc.com 或者 abc.com 的cookie,即父域名中的cookie可以被子域访问。
path:规则与domain相似,更精细的(子级)path可以访问范围更大的(父级)path中的cookie,而反过来不行,例如一个cookieA的范围是 domain=www.abc.com; path=/abc,一个cookieB的范围是domain=www.abc.com; path=/,如果当前处在www.abc.com/abc,则两个cookie都可以访问,如果当前处在www.abc.com/,则只能访问cookieB。
有一点需要注意,在history API中,history.pushState(state, title, url)的第三个参数是可以修改url的,但是这种修改并不影响path,也就是说,如果一开始在/,通过pushState修改为/home,那么也只能获取到/的cookie,这种状况在前端路由中比较常见。
设置cookie
直接将需要设置的cookie赋给document.cookie即可,设置方法如下:
document.cookie = 'key=value; domain=www.abc.com; path=/; secure=true; sameSite=Strict; max-age=session';
其中只有key和value是必须的,默认情况下
domain=location.host
path=location.pathname
secure=false
sameSite=None
max-age=session
ps: 经过chrome中测试,显式设置domain,浏览器会在前面加一个.,而不设置时是精确的当前域名
封装document.cookie
cookie本身是key=value形式的数据,每次使用document.cookie来进行操作会比较费劲,而且字符串操作cookie本身看起来并不雅观,所以可以对document.cookie进行封装以更好地使用,有很多库可以实现这个功能,简单而言可以看作以下实现:
class DocCookie {
set(key, value, path = null, domain = null, sameSite = 'None', maxAge = 'session', secure = false) {
const cookie = `${key}=${value}; ${path ? 'path=' + path + '; ' : ''}${domain ? 'domain=' + domain + '; ' : ''}sameSite=${sameSite}; max-age=${maxAge}; secure=${secure}`;
document.cookie = cookie;
}
get(key) {
const matched = document.cookie.match(new RegExp(`${key}\\s*=(.+?)(?=(;|$))`));
if (matched) {
return matched[1];
}
return null;
}
delete(key) {
document.cookie = `${key}=value; max-age=-1`;
}
}
const cookie = new DocCookie();
cookie.set('ABC', 'this is a new cookie');
cookie.get('ABC'); // "this is a new cookie"
使用Proxy
ES6中的Proxy为对象的key代理提供了一种方法:
class CookieManager {
constructor() {
this.sameSite = false;
this.secure = false;
this.path = null;
this.domain = null;
this.maxAge = 'session';
const self = this;
this.cookie = new Proxy(document, {
get(target, key) {
const matched = target.cookie.match(new RegExp(`${key}\\s*=(.+?)(?=(;|$))`));
if (matched) {
return matched[1];
}
return null;
},
set(target, key, value) {
const path = self.path || location.pathname;
const secure = self.secure;
const maxAge = self.maxAge;
const domain = self.domain || location.host;
const sameSite = self.sameSite;
const cookie = `${key}=${value}; path=${path}; secure=${secure}; max-age=${maxAge}; domain=${domain}; sameSite=${sameSite}`;
console.log(cookie);
target.cookie = cookie;
}
});
}
}
const cookieManager = new CookieManager();
cookieManager.cookie.ABC = 'this is a new cookie';
cookieManager.cookie.ABC; // "this is a new cookie"