01 - 一切的根源:HTTP 是”没有记忆”的
在讲”怎么实现登录”之前,得先想明白一个问题:为什么登录会是个问题?
如果服务端天生就认得每一个来访的人,那根本不需要”登录”这套东西。之所以要费这么大劲,是因为 HTTP 有一个天生的”缺陷”——它没有记忆。
一个让人崩溃的设定:每个请求都是陌生人
HTTP 是**无状态(stateless)**的。这句话翻译成人话就是:
服务端处理完一个请求后,转头就把你忘得一干二净。下一个请求过来,它完全不知道你是谁,也不知道你上一秒刚跟它说过话。
用一个类比:
你走进一家健忘症老板开的商场。
你:老板,我要进去逛。
老板:欢迎!(你进去了)
你逛了一圈,走到收银台。
你:老板,我要结账。
老板:您好,请问您是?我们见过吗? ← 他把你忘了
你走到 VIP 休息室。
你:我是会员,我要进去休息。
老板:您好,请出示会员证明。 ← 他又把你忘了
这个老板每一次见到你,都当成第一次。这就是 HTTP 服务端默认的样子。
落到接口上是什么感觉
假设你写了两个接口:
@app.post("/login") # 登录:验证用户名密码
@app.get("/my/orders") # 查我的订单
用户先调 /login,密码对了,登录成功。接着他调 /my/orders,问题来了:
第 1 个请求:POST /login { "username": "张三", "password": "..." }
服务端:密码对,登录成功!✅
(处理完,服务端把这次对话忘光了)
第 2 个请求:GET /my/orders
服务端:你是谁?你登录了吗?我怎么知道该返回谁的订单?🤷
这就是核心矛盾:登录成功是发生在”第 1 个请求”里的事,可服务端处理完第 1 个请求就失忆了。等第 2 个请求来的时候,它根本不记得”这个人刚刚登录过”。
那问题就变成了一句话
既然服务端会失忆,那我们要解决的问题其实就一句话:
登录成功之后,怎么让服务端在后续每一个请求里,都能重新认出”你就是刚才登录的那个人”?
整个登录体系,本质上都是在回答这一个问题。而回答的方式,无非是给你发一个”凭证”,你之后每次敲门都带上它:
登录成功那一刻:
服务端 ──── 发给你一张"凭证" ────▶ 你
之后每次请求:
你 ──── 敲门时亮出这张"凭证" ────▶ 服务端
服务端一看凭证:"哦,是张三,我认得!"
回到商场的类比,聪明的做法就是:你买票/办卡进场时,老板给你一个手环。之后你不管走到哪,只要亮出手环,老板就知道你是谁——他不需要记住你的脸,只需要认得那个手环。
那”凭证”具体长什么样?
这里就分出了两条经典路线,也是后面几章的主角:
登录成功,发凭证
│
┌───────────────┴───────────────┐
▼ ▼
路线 A:发一个"手环号" 路线 B:发一张"通行证"
(Session + Cookie) (Token / JWT)
服务端自己记账本: 凭证本身就写着你是谁,
"3 号手环 = 张三" 还带防伪签名,
手环上没写名字 服务端不用记账
- 路线 A:凭证只是一个无意义的编号,真正”你是谁”记在服务端的账本里 → 第 03 章
- 路线 B:凭证本身就装着”你是谁”,还带防伪签名,服务端验一下签名就行 → 第 04 章
小结
- HTTP 是无状态的:服务端处理完一个请求就”失忆”,默认不认得任何人。
- 所以登录的核心矛盾是:登录发生在某一个请求里,但服务端下一个请求就忘了。
- 整个登录体系要解决的就一句话:登录后,如何让服务端在后续每个请求里重新认出你。
- 解决方式是”发凭证 + 每次带上凭证”,具体分成 Session 和 Token 两条路线。
- 记住这个根源,后面所有设计你都能理解成”在想办法对抗服务端的失忆”。
下一章我们先别急着讲两条路线,而是把”登录”这个动作本身拆开——你会发现它其实是两件不同的事,很多人把登录讲糊,就是因为把这两件事搅在了一起。
下一章 → 02 - 登录到底在做什么:认证 vs 会话保持 | 回到 README 目录