Cookie
一个不大不小的问题
假设服务器有一个接口,通过请求这个接口,可以添加一个管理员。
但是,不是任何人都有权力做这种操作的。
那么服务器如何知道请求接口的人是有权力的呢?
答案是:只有登录过的管理员才能做这种操作。
可问题是,客户端和服务器的传输使用的是 http 协议,http 协议是无状态的。由于 http 协议的无状态,服务器忘记了之前的所有请求。它无法确定这一次请求的客户端,就是之前登录成功的那个客户端。
什么叫无状态?
就是 服务器不知道这一次请求的人,跟之前登录请求成功的人,是不是同一个人。
于是,服务器想到下面这样一个办法,按照流程来认证客户端的身份:
- 客户端登录成功后,服务器会给客户端一个出入证。
- 后续客户端的每次请求,都必须要附带这个出入证。
信息
服务器发扬了认证不认人的优良传统,就可以很轻松地识别身份了。但是,用户不可能只在一个网站登录,于是客户端会受到来自各个网站的出入证,因此,就要求客户端要有一个类似于卡包的东西,能够具备下面的功能:
- 能够存放多个出入证。这些出入证来自不同的网站,也可能是一个网站有多个出入证,分别用于出入不同的地方。
- 能够自动出示出入证。客户端在访问不同的网站时,能够自动地把对应的出入证附带请求发送出去。
- 正确地出示出入证。客户端不能将肯德基的出入证发送给麦当劳。
- 管理出入证的有效期。客户端要能够自动地发现那些已经过期的出入证,并把它从卡包内移除。
能够满足上面所有要求的,就是 cookie。
cookie 类似于一个卡包,专门用于存放各种出入证,并有着一套机制来自动管理这些证件。
卡包内的每一张卡片,称之为一个 cookie。
cookie 的组成
cookie 是浏览器中特有的一个概念,它就像浏览器的专属卡包,管理着各个网站的身份信息。
每个 cookie 就相当于是属于某个网站的一个卡片,它记录了下面的信息:
- key:键,比如
Identity
。 - value:值,比如
123dajgv3534aEOF
。 - domain:域,比如
www.baidu.com
,表示该 cookie 属于 baidu.com 这个网站。 - path:路径,表达这个 cookie 是属于该网站的哪个基路径的。比如
/
,表示该 cookie 属于根路径。 - secure:安全标志,表示是否使用安全传输。
- expires:过期时间,表示该 cookie 在什么时候过期。
当浏览器向服务器发送一个请求时,会检查该域名下所有的 cookie,如果发现合适的 cookie,就会自动将该 cookie 添加到请求头中,然后发送给服务器。
什么样的 cookie 会被浏览器自动添加到请求头中呢?
- cookie 没有过期。
- cookie 的域和这次请求的域匹配。
- cookie 的路径和这次请求的路径匹配。
- 验证 cookie 的 secure 标志,true 则要求当前请求协议必须是 https,否则不会自动添加;false 则没有要求,请求协议可以是 http,也可以是 https。
如何设置 Cookie
服务器端设置 Cookie
服务器端可以通过响应头中的 Set-Cookie
字段来设置 cookie。
Set-Cookie: key=value; domain=www.baidu.com; path=/; expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure; HttpOnly
每个 cookie 除了键值对是必须要设置的,其他的属性都是可选的,并且顺序没有限制。
属性说明
- 如果 path/domain 没有设置,默认为当前请求的 path/domain。
- expire 必须是有效的 GMT 格式,即格林威治时间。
- max-age 是相对有效期,和 expire 二选一即可。
- 如果 expire 和 max-age 都不设置,则表示会话结束后 cookie 过期。而对于大部分浏览器而言,关闭所有浏览器窗口就意味着会话结束。
- httpOnly 设置了 cookie 是否仅能用于传输,而不允许在客户端通过 JS 访问,这对防止跨站脚本攻击(XSS)很有帮助。
现在,还剩下最后一个问题,就是如何删除浏览器的一个 cookie 呢?
如果要删除浏览器的 一个 cookie,只需要服务器响应一个同样的域、同样的路径、同样的 key,但是时间是过期的 cookie 即可。
所以,删除 cookie 其实就是修改 cookie。
浏览器按照要求修改了 cookie 后,会发现其已经过期,于是自然就会删除了。
危险
无论是修改还是删除,都要注意 cookie 的域和路径,因为完全可能存在域或路径不同,但是 key 相同的 cookie。因此,无法仅通过 key 确定是哪一个 cookie。
客户端设置 Cookie
客户端可以通过 document.cookie
来设置 cookie。
document.cookie =
'key=value; domain=www.baidu.com; path=/; expires=Wed, 21 Oct 2025 07:28:00 GMT; Secure;';
可以看出,客户端设置和服务器设置的格式一样,只是有如下区别:
- 没有 HttpOnly。因为 HttpOnly 本来就是为了限制在客户端访问,既然你是在客户端配置,自然也就失去了限制的意义。
- path 的默认值。在服务器端设置 cookie 时,如果没有写 path,使用的是请求的 path。而在客户端设置 cookie 时,也许根本没有请求发生。因此,path 在客户端设置时的默认值是当前网页的 path。
- domain 的默认值。和 path 同理,客户端设置 cookie 时,domain 的默认值是当前网页的 domain。
- 其他:一样。
- 删除 cookie:和服务器设置一样,修改 cookie 的过期时间即可。
总结
以上就是 cookie 原理部分的内容。如果把 cookie 用于登录场景,就是如下的流程:
登录请求
- 浏览器发送请求到服务器,附带账号密码。
- 服务器验证账号密码是否正确。如果不正确,响应错误;如果正确,在响应头设置 cookie,附带登录认证信息。至于登录认证信息是长什么样、如何设计、要考虑哪些问题,就是另一个话题了。【JWT】
- 客户端收到 cookie,浏览器自动记录。
后续请求
- 浏览器发送请求到服务器,希望添加一个管理员,并将 cookie 自动附带到请求中。
- 服务器先获取 cookie,验证 cookie 中的信息是否正确。如果不正确,不予以操作;如果正确,完成正常的业务流程。