Cookie、Session、Token 之间有什么区别?
- 标签:网络
🏛 业务架构师
三者是会话状态的存放与传递方式,我按业务架构的演进逻辑讲:
- Cookie + Session:状态存服务端(内存/DB),浏览器持 ID。问题是有状态——集群下要么 Session 粘滞(负载不均、节点挂了会话丢),要么 Session 共享(Redis 集中存,多一跳网络和一套运维)。中小系统够用,规模化后是拖累;
- Token(以 JWT 为代表):状态签名后放客户端(签名保证不可篡改,不加密),服务端无状态——任意节点验签即可,水平扩容零障碍,这是微服务和前后端分离普及的真正原因。
但 JWT 的业务代价要讲透,我评审时必查三条:
- 无法主动失效:签发后到过期前一直有效,"用户改密码后强制全部下线""封号立即生效"做不到。我的方案:JWT 只做短时效(如 2 小时)+ Redis 黑名单/白名单——退一小步的有状态,换回运营控制力。纯 JWT 的"完全无状态"是理想主义,业务上几乎总需要失效能力;
- 无法续期交互:配 refresh token 双令牌(access 短 + refresh 长),安全与体验平衡;
- 体积:JWT 比 session id 大数倍,每个请求都带,高频接口的带宽要算。
选型结论:C 端 App/前后端分离 → 双令牌 JWT + Redis 控制;管理后台等强控场景 → 服务端会话 + Redis 集中存储更直接。登录态是业务的安全边界,方案按"失效需求的强度"定,而不是按技术潮流定。
🏗 系统架构师
三者是"会话状态存放位置"的演进,我按架构演进讲:
- Cookie + Session:服务端存会话(内存/集中式存储),浏览器只存 session_id(Cookie)。问题:服务端有状态——集群要 Session 粘性或集中存储(Redis),水平扩展受限;后来 Spring Session + Redis 让集群共享 Session,但仍是中心状态;
- Token(以 JWT 为代表):登录后签发自包含的令牌(header.payload.signature),服务端无状态——不存会话,每次请求验签即可。收益:天然适配分布式/跨域/移动端/小程序;代价:无法主动吊销(除非加黑名单/短有效期 + refresh token 轮换),载荷随每请求传输不宜过大;敏感信息不能放 payload(仅 Base64,不是加密);
- 现代工程组合:短寿命 access token(5~30 分钟)+ 长寿命 refresh token(服务端可撤销)+ Redis 黑名单做强制下线——拿到无状态的扩展性,同时保留控制力。
我的架构选择:单体/管理后台用 Session + Spring Session 足够;开放 API、多端、网关统一鉴权一律 JWT + 网关验签,业务服务无感透传用户上下文(header 注入 userId)。
💻 全栈开发专家
三者是会话状态的存储和传递方案演进:
- Cookie:浏览器按域名存储的小数据块,随请求自动带上。仅是客户端存储/载体;受同源策略约束、4KB 限制。
- Session:状态存服务端(内存/Redis),浏览器只存 session id(Cookie 传递)。服务端有状态——集群要 spring-session + Redis 共享,否则落到第二台机器就"掉线"。
- Token(JWT 为主):状态编码进自包含的 token(header.payload.signature),服务端只验签不查会话存储,天然适合分布式/APP/小程序。
java
// JWT 载荷(Base64,可解码别放敏感明文)
{ "sub": "42", "role": "user", "exp": 1790000000 }
// 服务端校验 HMAC/RSA 签名,无状态完成认证JWT 的工程坑(必答):
- 无法主动失效——签发后到期前一直有效。我的方案:短过期(2h)+ refresh token 轮换 + 登出/踢人用 Redis 黑名单(只存 jti,体量小);
- 续期不能无感延长 exp(等于永不过期),用 refresh 换新 access;
- payload 只是编码不是加密,敏感信息要么不放要么 JWE。
我的选型:传统单体 web 用 Session+Redis 省心;前后端分离/多端/开放 API 一律 JWT + 黑名单 + 网关统一验签(下游从 header 取 userId)。