从一个问题开始:B 站是怎么认识我的?
你有没有想过这样一个问题:
-
我打开 B 站,输入账号密码登录。
-
然后我刷新页面、点开一个视频、再点开另一个视频......
-
B 站居然一直知道"我是我",知道我的昵称、头像、收藏夹。
可是,HTTP 协议是无状态、无连接的。
无状态 :每个请求都是独立的,服务器不会记得你上一次请求是谁。
无连接:每次请求响应后,连接就断开了(HTTP/1.1 虽然支持 keep-alive,但本质仍是请求-响应模型)。
那服务器凭什么记住我?
答案就是:Cookie 和 Session。
HTTP Cookie:服务器寄存在浏览器的小纸条
什么是 Cookie?
HTTP Cookie(也叫 Web Cookie、浏览器 Cookie)是服务器发送到用户浏览器并保存在浏览器上的一小块数据。
它会在浏览器之后向同一服务器再次发起请求时,被自动携带并发送给服务器。
通俗理解:Cookie 就像服务器给浏览器发的一张"小纸条",浏览器把它存起来,下次再去同一家店(服务器)时,自动把这张小纸条带上。
工作原理(三步走)
-
第一次访问 :服务器在响应的 HTTP 头中设置
Set-Cookie字段,把 Cookie 发送给浏览器。 -
浏览器保存:浏览器接收到 Cookie 后,按照域名保存在本地。
-
后续请求 :浏览器自动在 HTTP 请求头中携带
Cookie字段,把之前保存的 Cookie 发送给服务器。
用一张简图表示:
浏览器 服务器
| ---- 第一次请求 ---------> |
| <--- 响应 Set-Cookie ----- |
| (保存 Cookie) |
| |
| ---- 后续请求 Cookie ----> |
| <--- 响应 ---------------- |
Cookie 的分类
| 类型 | 特点 |
|---|---|
| 会话 Cookie(Session Cookie) | 浏览器关闭时失效,不设置 expires 或 max-age |
| 持久 Cookie(Persistent Cookie) | 带有明确的过期日期或持续时间,可跨多个浏览器会话存在 |
如果 Cookie 是持久性的,它其实就是浏览器相关的、特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为 Cookie 文件通常以二进制或 SQLite 格式存储。一般我们直接在浏览器对应的选项中查看即可。
Cookie 的用途
-
用户认证和会话管理(最重要)
-
跟踪用户行为
-
缓存用户偏好等
比如在 Chrome 浏览器下,可以直接访问:chrome://settings/cookies 查看和管理 Cookie。
Cookie 的安全性
由于 Cookie 是存储在客户端的,因此存在被篡改或窃取的风险。
这也是为什么后来引入了 Session。
深入 Cookie:Set-Cookie 头部字段详解
基本格式
Set-Cookie: <name>=<value>
其中 <name> 是 Cookie 的名称,<value> 是 Cookie 的值。
完整的 Set-Cookie 示例
Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00 UTC; path=/; domain=.example.com; secure; HttpOnly
各属性详解
| 属性 | 描述 |
|---|---|
username=peter |
Cookie 的名称和值,标识用户名为 "peter" |
expires=Thu, 18 Dec 2024 12:00:00 UTC |
指定 Cookie 的过期时间。在这个例子中,Cookie 将在 2024 年 12 月 18 日 12:00:00 UTC 后过期 |
path=/ |
定义 Cookie 的作用范围。这里设置为根路径 /,意味着 Cookie 对 .example.com 域名下的所有路径都可用 |
domain=.example.com |
指定哪些域名可以接收这个 Cookie。点前缀(.)表示包括所有子域名 |
secure |
指示 Cookie 只能通过 HTTPS 协议发送,不能通过 HTTP 协议发送,增加安全性 |
HttpOnly |
阻止客户端脚本(如 JavaScript)访问此 Cookie,有助于防止跨站脚本攻击(XSS) |
时间格式:RFC 1123 标准
时间格式必须遵守 RFC 1123 标准,具体格式样例:
Tue, 01 Jan 2030 12:34:56 GMT
或者推荐使用 UTC:
Thu, 18 Dec 2024 12:00:00 UTC
各部分的含义:
-
Tue:星期二(星期几的缩写) -
,:逗号分隔符 -
01:日期(两位数表示) -
Jan:一月(月份的缩写) -
2030:年份(四位数) -
12:34:56:时间(小时、分钟、秒) -
GMT:格林威治标准时间(时区缩写)
注意事项
-
每个 Cookie 属性都以分号(
;)和空格()分隔。 -
名称和值之间使用等号(
=)分隔。 -
如果 Cookie 的名称或值包含特殊字符(如空格、分号、逗号等),则需要进行 URL 编码。
Cookie 的生命周期
-
如果设置了
expires属性,则 Cookie 将在指定的日期/时间后过期。 -
如果没有设置
expires属性,则 Cookie 默认为会话 Cookie,即当浏览器关闭时过期。
单独使用 Cookie 有什么问题?
我们写入的是测试数据,如果写入的是用户的私密数据呢?比如:
-
用户名密码
-
浏览痕迹
-
购物车内容
本质问题在于:这些用户私密数据在浏览器(用户端)保存,非常容易被人盗取。更重要的是,除了被盗取,还有就是用户私密数据也就泄漏了。
所以,我们需要一种更安全的机制:Session。
HTTP Session:服务器端的"用户档案"
什么是 Session?
HTTP Session 是服务器用来跟踪用户与服务器交互期间用户状态的机制。
由于 HTTP 协议是无状态的(每个请求都是独立的),因此服务器需要通过 Session 来记住用户的信息。
通俗理解:Session 就像服务器给每个用户建立的一份"档案",档案保存在服务器端,用户手里只拿一个"档案编号"(Session ID)。
工作原理
-
首次访问:服务器为用户创建一个唯一的 Session ID,并通过 Cookie 将其发送到客户端。
-
后续请求:客户端携带这个 Session ID,服务器通过 Session ID 识别用户,从而获取用户的会话信息。
-
存储位置:服务器通常会将 Session 信息存储在内存、数据库或缓存中。
用一张简图表示:
浏览器 服务器
| ---- 第一次请求 ---------> |
| <--- Set-Cookie: sessionid=xxx |
| (保存 sessionid) | (创建 Session 对象)
| |
| ---- 后续请求 Cookie: sessionid=xxx ----> |
| <--- 响应 ---------------- | (通过 sessionid 找到 Session 对象)
安全性
-
与 Cookie 相似,由于 Session ID 是在客户端和服务器之间传递的,因此也存在被窃取的风险。
-
但是一般虽然 Cookie 被盗取了,但是用户只泄漏了一个 Session ID,私密信息暂时没有被泄露的风险。
-
Session ID 便于服务端进行客户端有效性的管理,比如异地登录。
-
可以通过 HTTPS 和设置合适的 Cookie 属性(如
HttpOnly和Secure)来增强安全性。
超时和失效
-
Session 可以设置超时时间,当超过这个时间后,Session 会自动失效。
-
服务器也可以主动使 Session 失效,例如当用户登出时。
用途
-
用户认证和会话管理
-
存储用户的临时数据(如购物车内容)
-
实现分布式系统的会话共享(通过将会话数据存储在共享数据库或缓存中)
总结:Cookie vs Session
| 对比项 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端(浏览器) | 服务器端 |
| 安全性 | 较低,容易被篡改或窃取 | 较高,私密信息不直接暴露 |
| 存储容量 | 有限(通常 4KB) | 较大(取决于服务器内存/数据库) |
| 生命周期 | 可设置过期时间,会话 Cookie 关闭浏览器失效 | 可设置超时时间,服务器可主动失效 |
| 用途 | 用户认证、跟踪用户行为、缓存用户偏好 | 用户认证、会话管理、存储临时数据 |
| 跨域支持 | 受同源策略限制 | 通常通过 Cookie 传递 Session ID,也受同源策略限制 |
实际应用中,Cookie 和 Session 通常会结合使用:
-
服务器创建 Session,把 Session ID 通过 Cookie 发送给浏览器。
-
浏览器后续请求自动携带 Session ID。
-
服务器通过 Session ID 找到对应的 Session 对象,从而识别用户。
这样既利用了 Cookie 的自动携带机制,又保证了私密数据存储在服务器端,达到了最佳的用户体验和安全性。