做后端项目,登录基本绕不开。
最简单的登录流程可能只是:
用户名
+
密码
↓
验证
↓
登录成功但真正的问题是:
用户登录成功之后,服务器怎么知道下一次请求还是这个人?
这就涉及Session、Cookie和Token。
HTTP是无状态的
HTTP请求本身没有状态。
比如用户第一次请求:
GET /profile服务器不知道你是谁。
下一次请求:
GET /profile服务器还是不知道。
所以登录系统需要额外建立"身份状态"。
Cookie
Cookie是浏览器保存的一小段数据。
服务器可以返回:
Set-Cookie: session_id=abc123浏览器保存之后,以后请求会自动带上:
Cookie: session_id=abc123于是服务器就可以通过:
session_id
↓
查找Session
↓
找到用户Session
Session通常保存在服务器端。
比如数据库里:
sessions
id
user_id
expires_at登录成功:
用户登录
↓
验证用户名密码
↓
生成session_id
↓
保存Session
↓
返回Cookie之后:
请求
↓
Cookie
↓
session_id
↓
服务器查询Session
↓
得到user_id
↓
知道当前用户这种方式对传统Web应用很直观。
Token
另一种常见方案是Token。
比如登录成功之后返回:
{
"token": "xxxxxxxx"
}客户端之后请求API:
Authorization: Bearer xxxxxxxx服务器通过Token判断身份。
这种方式很适合:
- 前后端分离
- 移动端
- SPA
- API服务
Cookie和Token不是对立的
一个容易误解的地方:
Cookie和Token是两种认证方式。
实际上它们解决的问题不完全一样。
Cookie是一种客户端保存和发送数据的机制。
Token是一种身份凭证。
完全可以:
Token
↓
存在Cookie里也可以:
Token
↓
Authorization Header所以真正该理解的是:
身份凭证是什么,以及它怎么在客户端和服务器之间传递。
FastAPI中获取Cookie
比如:
from fastapi import Cookie
@app.get("/profile")
def profile(session_id: str | None = Cookie(default=None)):
if not session_id:
return {"message": "Not logged in"}
return {
"session_id": session_id
}登录时设置Cookie:
from fastapi import Response
@app.post("/login")
def login(response: Response):
session_id = "generated-session-id"
response.set_cookie(
key="session_id",
value=session_id,
httponly=True,
secure=True,
samesite="lax",
)
return {"success": True}实际项目当然不能真的用固定字符串当Session ID。
得用安全的随机值。
HttpOnly
这里有个很重要的Cookie属性:
HttpOnly设置之后,JavaScript没法直接读到这个Cookie。
可以降低某些XSS场景下Cookie被直接读取的风险。
所以如果Cookie用来保存登录凭证,通常应该认真考虑:
HttpOnly
Secure
SameSite这些属性。
Session和JWT怎么选
如果是传统Web后端:
Session + Cookie很简单。
如果是纯API服务:
Token
+
Authorization Header通常更自然。
但也不能简单说:
JWT一定比Session好。
JWT最大的特点之一是服务器可以减少对服务端Session状态的依赖。
但JWT同样会带来:
- Token失效问题
- Refresh Token
- 注销处理
- Token泄露风险
- 权限变化后的处理
所以认证系统不是:
JWT = 高级
Session = 老旧而是根据项目实际需求来选。
登录系统真正重要的是什么
我觉得学登录系统的时候,不应该一开始就背各种框架API。
先搞清楚:
用户提交凭证
↓
服务器验证
↓
建立身份状态
↓
客户端保存凭证
↓
后续请求携带凭证
↓
服务器识别用户理解了这个流程,以后不管用Session、JWT、OAuth还是别的方案,都会容易很多。