1. Cookie 的参数
    1. Path
    2. Domain
    3. HttpOnly
    4. Secure
    5. Max-Age
    6. Expires
    7. SameSite
  2. 通过 Js 操作 Cookie
  3. 通过服务器操作 Cookie
  4. 参考

Cookie

HTTP 协议是"无状态的",但需要通过某些标记来判断用户身份,Cookie 为此而生.
Cookie 是保存在浏览器上的一段数据,常用来识别用户身份.当浏览器向服务器发出 HTTP 请求时,会自动携带 Cookie.
修改 Cookie 的方法有两种, 在服务器发送响应数据时设置 response headers,或在浏览器中执行 Js.
浏览器 Cookie 可以储存的数据有条数和大小限制.一般限制为50条以下 , 大小不超过4096bytes.

Cookie 有大小限制,且浏览器会自动发送 Cookie,最好不要把 Cookie 当作 LocalStore 使用.

Path

设置 Cookie 所应用的路径

假设有以下三种路径:

1
2
3
/admin
/admin/data
/user

/admin/data 可以获取 /admin 路径的 Cookie
/admin 不能获取 /admin/data 路径的 Cookie
/admin 不能获取 /user 路径的 Cookie, 但反过来 /user 也不获取到 /admin

总结就是,子路径可以获取父路径的 Cookie.但反过来不行, 父路径取不到子路径的 Cookie.
从子路径(/admin/data)访问服务器时, 父路径(/admin)的 Cookie 也会被自动发送给服务器.
从父路径(/admin)访问服务器时,不会发送子路径(/admin/data)中的 Cookie.

虽然不能从父路径获取子路径的 Cookie, 但可以在父路径下配置子路径的 Cookie.

例如, 在 /admin 路径下配置一个属于 /admin/data 的 Cookie.
这个 Cookie 在 /admin 路径下读不到
1.PNG

但可以在 /admin/data 中通过 document.cookie 读取
2.PNG

当然,也可以从子路径配置父路径的 Cookie.
下面是一个在 /admin/data 路径配置 /admin 路径 Cookie 的例子.
1.PNG
2.PNG

path的默认值为 /,如果没有设置 path的话, 所有子路径都可以访问这个 Cookie.

Domain

Path 配置的是路径,而 Domain 配置的是域.

有如下域名:

1
2
main.test
sub.main.test

如果配置 domain=main.test, 则可以在 main.test 和 sub.main.test都能获取到这个 Cookie. 如果配置 domain=sub.main.test, 则能在 sub.main.test 域名下访问这个 Cookie,而 main.test 不行.

可以通过子域(sub.main.test)可以访问父域(main.test)的 Cookie
1.PNG
2.PNG

也可以在子域配置父域的 Cookie
3.PNG
4.PNG

但不能在父域访问子域的 Cookie, 也不能在父域创建/修改子域的 Cookie. 7.PNG
8.PNG

注意: 如果不配置 domain,则表示这个 Cookie 的域为当前所在的域名,且不包括子域.
也就是如果没配置 domain=main.test 的话, 就只能在 main.test 访问这个 Cookie, 不能用 sub.main.test 域名访问.
9.PNG
10.PNG

HttpOnly

禁止通过 Js 操作 Cookie 捕获.PNG

未设置 HttpOnly 标记 和设置了 HttpOnly 标记后的效果对比.
设置了 HttpOnly 后, document.cookie 无法读取到对应的 Cookie.
1.PNG

Secure

只有通过 HTTPS 协议访问时(而非 HTTP),浏览器才会发送 Cookie 给服务器.
捕获.PNG

HTTP 和 HTTPS 的区别, HTTP 不会发送设置了 Secure 标记的 Cookie.
2.PNG

Max-Age

设置 Cookie 的有效期,单位为

1
2
// 一天之后过期
document.cookie=`uid=1;max-age=${60 * 60 *24}`

捕获.PNG

注意,ie8 及以下版本的浏览器不支持 Max-Age

Expires

设置 Cookie 的过期时间, 接受 UTC 时间作为参数

1
2
// 一天后过期
document.cookie=`uid=1;expires=${new Date(Date.now() + 1000 * 60 * 60 * 24).toUTCString()}`

如果同时存在 Max-Age 和 Expires, 则 优先采用 Max-Age.

1
2
3
// max-age 一小时
// expires 一天
document.cookie=`uid=1;expires=${new Date(Date.now() + 1000 * 60 * 60 * 24).toUTCString()}`

1.PNG

注意, 过期时间以服务器时间为准

如果未设置 Max-Age 和 Expires, 则表示这是个 session cookie(会话 Cookie), 这个 Cookie 会在浏览器关会话结束(关闭浏览器)时被删除.

SameSite

可选值:

  • Strict 禁止发送第三方 Cookie
  • Lax
  • None

Lax 除以下情况外,均不携带 Cookie:
1 同域名(即 samesite=strict )
2 GET方法的跳转到同域名, 例如:

1
2
<a href=""></a> 
<form method="GET" action=""></form>
1
<a href="https://main.test"></a>

3 prerender 预加载

1
<link rel="prerender" href="">

例子: 服务器配置, 设置了两个 Cookie, 只有第一个Cookie foo 设置了 sameSite:

1
2
3
4
5
6
7
8
9
10
11
app.use((req, res) => {
console.log(req.cookies)
res.cookie("foo", "1", {
sameSite: "Lax", // 注意这里
domain: "main.test"
});
res.cookie("bar", "2", {
domain: "main.test"
});
res.send();
});

接收到的 Cookie
捕获.PNG
下面将尝试通过不同的方法访问 main.test, 看看是否会发送带有 sameSite=Lax 的 Cookie

1 通过 a 标签, 从 127.0.0.1 转跳到 https://main.test, Cookie 被发送.
Image 8.png

2 在 127.0.0.1 域名下, 通过 GET 方法向 https://main.test 提交表单, Cookie 被发送.

1
2
3
<form method="GET" action="https://main.test">
<button>提交</button>
</form>

Image 11.png

3 在 127.0.0.1 域名下, 通过 POST 方法向 https://main.test 提交表单, Cookie foo 没有被发送.

1
2
3
<form method="POST" action="https://main.test">
<button>提交</button>
</form>

Image 10.png

4 在 127.0.0.1 域名下, 通过 fetch GET/POST 向 https://main.test 发送数据, Cookie foo 没有被发送.

1
2
3
4
5
6
7
8
9
// 访问代码
fetch('https://main.test/', {
method: 'GET',
credentials: 'include'
})
fetch('https://main.test/', {
method: 'POST',
credentials: 'include'
})

Image 13.png

5 prerender
这个挺不好看到效果.因为浏览器有缓存,等缓存过期要好久,控制台也不会显示 prerender 链接的预加载.

首先修改下 127.0.0.1/index.html, 加上

1
<link rel="prerender" href="https://main.test" />

服务端也要改一下,不从服务端设置 Cookie.要是直接访问 https://main.test 的话会留下缓存, 浏览器就不会去请求设置了 prerender 的 link.

1
2
3
4
app2.use((req, res) => {
console.log(req.cookies, req.headers.referer)
res.send();
});

用 Chrome 打开一个无痕窗口, 然后 右键查看 -> NetWork -> 勾上 Offline.
然后访问 https://main.test, 此时 Chrome 会提示未连接到互联网.
然后用 Chrome 扩展 EditThisCookie 手工加上两个 Cookie.

Image 16.png

再打开一个隐身窗口, 访问 http://127.0.0.1:5500/
查看服务器日志, 表明 Chrome 确实预加载了带有 prerender 的 link.

捕获.PNG

兼容性
捕获.PNG

查询

1
document.cookie // 'foo=1'

添加/修改

1
document.cookie="bar=1;max-age=3600;domain=main.test"

删除 Cookie

1
document.cookie = 'bar=;domain=main.test;expires=Thu, 01 Jan 1970 00:00:01 GMT;';

注意 domain 和 path 要匹配

服务器可以在发送数据时,在 response headers 中使用 Set-Cookie 来更改客户端的 Cookie.
以 Express 为例:

1
2
3
4
app.use((req, res) => {
res.cookie('uid', '111111')
res.send()
})

捕获.PNG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.use((req, res) => {
res.cookie("foo", "1", {
domain: 'main.test',
secure: true,
path: 'admin',
sameSite: 'Lax',
httpOnly: true,
secure: true,
maxAge: 1000 * 60 * 60, // 保质期, 单位为 毫秒
expires: new Date(Date.now() + 1000 * 60) // 接受 Date 作为参数, 优先级比 maxAge 低
});
res.cookie("bar", "2", {});
res.send();
});

Image 19.png

删除 Cookie

1
2
3
4
app2.use((req, res) => {
res.clearCookie('foo');
res.send();
});

捕获.PNG

参考

Cookie 的 SameSite 属性 - 阮一峰的网络日志
详解 Cookie 纪要 | 晚晴幽草轩
HTTP cookies - HTTP | MDN