关于document.cookie如何使用

653 阅读2分钟

在前端脚本中,为了更好的安全性,开发者对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"