从Cookie到Token的全面指南
目录导读
为什么需要会话状态维持?
HTTP协议天生“无状态”——每次请求都是独立的,服务器不认识“你是谁”,但在实际场景中,用户登录后需要连续操作(比如购物车、评论区),这时脚本就必须记住用户的身份和状态。

核心痛点:
- 用户刷新页面后需要保持登录
- 跨页面请求需要携带用户信息
- 多设备间同步会话数据
传统方法:Cookie与Session
1 工作原理
- 服务器:在用户首次请求时创建Session对象(存储在内存、数据库或Redis),生成唯一Session ID。
- 客户端:服务器通过
Set-Cookie头将Session ID写入Cookie(通常加密)。 - 后续请求:浏览器自动携带Cookie,服务器比对Session ID获取用户信息。
2 代码示例(Python Flask)
from flask import Flask, session, make_response
app = Flask(__name__)
app.secret_key = 'your-secret-key'
@app.route('/login')
def login():
session['user'] = 'Alice'
return '登录成功!'
优点:成熟稳定,大多数语言框架内置支持。
缺点:
- 分布式部署时Session共享困难(需引入Redis等中间件)
- 移动端或API接口中Cookie支持不佳
3 关键资源
- 维基百科:HTTP Cookie
- MDN Web Docs:document.cookie
现代方案:Token与JWT
1 JWT(JSON Web Token)核心机制
- 结构:
Header.Payload.Signature - 动作:用户登录后,服务器生成Token返回客户端;客户端将Token存入本地存储或内存变量;每次请求附加在
Authorization: Bearer <token>头中。 - 验证:服务器用密钥验证Token签名,解析Payload获取用户ID。
2 为什么JWT适合现代脚本?
- 无状态:服务器无需存储会话,Token自身包含用户信息。
- 跨域友好:适合API网关、微服务架构。
- 有效期控制:可设置
exp字段,定期刷新Token。
3 代码示例(Node.js + jsonwebtoken库)
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 123 }, 'secret_key', { expiresIn: '1h' });
const decoded = jwt.verify(token, 'secret_key');
风险提醒:
- Token被窃取后无法主动失效(除非黑名单机制)
- Payload明文化,勿存敏感信息
脚本实战:Python与Node.js实现
1 Python脚本:Cookie+Session完整流程
import requests
# 模拟登录
login_url = 'example.com/login'
payload = {'username': 'admin', 'password': '123'}
response = requests.post(login_url, data=payload)
cookie = response.cookies.get('session_id')
# 后续请求携带Cookie
session = requests.Session()
session.cookies.set('session_id', cookie)
data = session.get('example.com/dashboard').json()
2 Node.js脚本:JWT自动刷新
let token = '...';
let refreshToken = '...';
async function fetchWithJWT(url) {
let response = await fetch(url, {
headers: { Authorization: `Bearer ${token}` }
});
if (response.status === 401) {
// 刷新Token
const newToken = await refreshJWT(refreshToken);
token = newToken;
return fetchWithJWT(url);
}
return response.json();
}
3 最佳实践:混合存储
- 登录页:用Cookie持久化(降低XSS风险)
- 受保护API:用Token增强防篡改能力
安全陷阱与优化策略
1 常见攻击与防御
| 攻击类型 | 防御措施 |
|---|---|
| 会话固定攻击 | 用户登录后重新生成Session ID |
| XSS窃取Cookie | 设置HttpOnly、Secure标志 |
| CSRF | 添加CSRF Token(如Django的csrf_token) |
| JWT重放攻击 | 引入nonce或jti(JWT ID)一次性验证 |
2 性能优化:Token压缩与缓存
- 使用 jose 库将JWT头部压缩为
HS256算法,减少传输体积。 - 对频繁请求设置短期Token(如5分钟),配合长期Refresh Token减少解密次数。
3 分布式系统解决方案
- Sticky Session:通过负载均衡将同一用户请求路由到同一服务器(不推荐)。
- 集中式缓存:将Session存入Redis集群,所有服务器共享。
- JWT集群:所有节点用相同密钥验证Token,但需配合黑名单(如Redis记录失效Token)。
常见问题问答
Q1:为什么我的脚本退出后Token失效?
A:Token可能存储在sessionStorage中(浏览器或App的临时存储),关闭页面即被清除,改为localStorage或Cookie可持久化。
Q2:Cookie大小有限制,如何存储大量会话数据?
A:避免直接将用户对象存入Cookie,只需存储Session ID或Token,数据保留在服务器端数据库或缓存中。
Q3:移动端App如何维持会话?
A:优先使用Token(JWT),存入App的安全钥匙串(如iOS Keychain)或EncryptedSharedPreferences(Android),避免暴露明文字典。
Q4:脚本如何实现“记住我”功能?
A:生成一个长期有效的Refresh Token(如30天),与短期Token分离,登录时服务器同时返回access_token(短有效期)和refresh_token(长有效期),通过脚本自动刷新机制维持会话。
Q5:无状态Token(JWT)如何注销?
A:
- 简单做法:客户端删除Token即可(不主动通知服务器)。
- 更可靠做法:服务端维护“黑名单”(如Redis中存储已注销的Token ID),每次验证时查询。
- 升级方案:用动态Token(如每步操作绑定一次性nonce)彻底解决。
最终建议:选择哪种会话维持方式,取决于你的场景——传统网站推荐Cookie+Session(自带CSRF保护),现代API推荐JWT(轻量跨域),混合应用则用Token+刷新机制,无论哪种,务必开启HTTPS,防止传输层窃听。