K 的一隅

网络 网络基础与可靠性

Cookie、Session、Token:登录状态到底放在哪里

从门票、登记簿和临时通行证说起,理解 Cookie、Session、JWT 与刷新令牌各自承担的角色。

9 分钟阅读 更新于 2026-07-24
本文目录

进入展馆时,你可能拿到一张随身门票;前台还会在登记簿里记下你的编号;临时进入后台区域,则需要另一张短时通行证。登录状态也有类似分工:Cookie 负责随请求携带信息,Session 保存服务端状态,Token 把一部分凭证交给客户端。

三种东西如何配合

Cookie 是一种 HTTP 状态载体,不等于 Session;Session 是服务端保存的一种会话方案;Token 是客户端提交给服务端验证的凭证。项目可以组合它们,也可以只采用其中一部分。

服务器通过 Set-Cookie 告诉浏览器保存值,后续符合域名、路径和安全条件的请求会自动带上:

http
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Lax; Path=/

HttpOnly 限制 JavaScript 读取,Secure 要求 HTTPS,SameSite 控制跨站请求时是否携带。Cookie 方便,但它会随着请求自动发送,过大的 Cookie 会增加每次请求的体积。

Session 把秘密留在服务器

常见做法是 Cookie 只保存一个随机 Session ID,真正的用户、角色、过期时间放在服务器或共享存储中。用户登出时删除或使 Session 失效即可。缺点是多台服务器需要共享会话,或者使用粘性会话;服务端还要处理过期、并发登录和存储容量。

Token 把验证材料带到请求里

JWT 等 Token 通常包含声明并带有签名,服务端可以验证它是否被篡改、是否过期。它适合跨服务传递身份,但“签名正确”不等于“权限永远有效”。Token 被盗后,在过期前可能继续被使用;撤销、刷新和轮换策略不能省略。

访问令牌和刷新令牌通常有不同寿命。访问令牌短一些,刷新令牌只用于换取新的访问令牌。刷新令牌的保存位置、轮换失败和多设备登出,往往比“JWT 怎么生成”更值得认真设计。

凭证放在哪里不是单选题

HttpOnly Cookie 能减少脚本直接读取凭证的机会,但 Cookie 会自动参与请求,需要关注 CSRF 和 SameSite;内存里的访问令牌离开页面就消失,暴露面较小,但刷新页面需要重新获取;localStorage 方便跨刷新,却必须认真面对 XSS 风险。没有一种存储位置在所有场景下都最好。

选择时要看攻击模型、应用形态和用户体验。传统同站 Web 应用常用安全 Cookie 保存 Session;前后端分离或多端系统可能使用短时访问令牌与刷新令牌;高风险操作还应增加二次验证,而不是只依赖一次登录。

CSRF 与 XSS 盯的是不同入口

Cookie 自动携带的特点让 CSRF 成为需要关注的问题:攻击者可能诱导浏览器向目标站点发请求,而浏览器会带上 Cookie。SameSite、CSRF Token、Origin/Referer 校验和正确的请求方法可以共同降低风险。

XSS 则是恶意脚本进入了你的页面上下文,可能读取非 HttpOnly 凭证、操作页面和发起同源请求。HttpOnly 能保护 Cookie 不被脚本读取,却不能修复 XSS 本身。输入输出编码、CSP 和安全的模板使用仍然重要。

登出需要撤销什么

只删除浏览器 Cookie,不一定让已经签发的 JWT 立刻失效;只让服务端 Session 失效,也可能留下其他设备上的刷新令牌。登出策略要明确是当前设备退出、所有设备退出,还是撤销某个会话,并记录会话版本、设备和最后使用时间。

刷新令牌轮换还能发现重放:每次刷新后作废旧令牌,如果旧令牌再次出现,就可以认为它可能被复制,并撤销相关会话。这个机制增加了服务端状态,却换来了更清晰的失窃检测能力。

凭证过期时页面应该怎么做

多个接口同时收到 401 时,不应每个请求各自刷新一次 Token。通常由一个刷新队列统一处理:第一次请求负责刷新,后续请求等待结果;刷新失败则清理会话并回到登录页。还要防止刷新接口本身触发无限重试。

界面层也不要把所有 401 都当成“用户密码错了”。可能是访问令牌过期、账号被禁用、资源属于别的用户,或服务器要求重新验证。HTTP 状态、业务错误码和用户提示要有对应关系。

登录状态不是用户身份本身

浏览器里有一个 Cookie,只能说明浏览器持有某个凭证;服务端仍需查询用户、校验权限、确认资源归属。前端把按钮隐藏起来不是权限控制,真正的授权必须发生在服务端。

登录系统最终管理的不是一个“是否登录”的布尔值,而是一组会变化的凭证关系:用户、设备、会话、访问令牌、刷新令牌和资源权限。每个凭证都有签发者、有效期、使用范围和撤销方式。把这些关系压缩成一个长期存在的字符串,代码看似简单,失效、盗用和多端同步时就会暴露问题。

因此认证方案的原理重点不在于选择 Cookie 还是 JWT,而在于回答几件事:凭证如何产生,服务器如何验证,何时过期,如何主动撤销,泄露后如何降低影响,权限变化后如何及时生效。技术选型只是这些问题的实现方式,不是问题本身。

会话验证通常发生在每一次受保护请求的入口。服务端先从 Cookie 或 Authorization 取出凭证,再验证签名或查询 Session,确认没有过期、没有被撤销,最后把得到的用户身份交给授权层。认证中间件通过,不代表业务方法可以直接信任客户端传来的 userId;资源归属仍要用服务端推导出的身份判断。

刷新令牌的存在,是在安全和体验之间做折中。访问令牌短,泄露后的有效窗口较小;刷新令牌长,用户不用频繁输入密码,但它更值得保护。轮换刷新令牌时,服务端要记录当前有效链,发现旧令牌再次使用就触发撤销;否则攻击者拿到旧令牌后可以长期复制正常登录流程。

跨站请求的关键不只是 Cookie 是否携带,还包括请求是否会改变状态。读取公开资源与修改邮箱、删除订单的风险不同。SameSite 限制、CSRF Token、Origin 校验和要求非简单请求预检,可以形成多层防线,但任何一层都不能替代服务端的授权检查。

多端登录还会引入设备管理:手机和电脑可以有不同会话,用户可能只撤销其中一台;管理员封禁账号后,旧访问令牌是否立即失效;修改密码后,其他刷新令牌是否全部回收。若数据模型只有一个 isLoggedIn,这些问题就无处表达。会话表通常需要设备、创建时间、最后使用、IP/UA 摘要和撤销原因等信息。

安全日志也要避免记录完整 Cookie、Authorization 和刷新令牌。可以记录会话 id 的哈希、用户 id、请求 id、失败原因和设备摘要,用于审计而不直接扩大凭证泄露面。日志系统本身也是凭证经过的地方,不能因为它“只在内部”就放弃脱敏。

认证链路中的每一步都可能失败

登录接口可能成功,但设置 Cookie 被浏览器拒绝;Cookie 存在,但因为 Domain 或 SameSite 不匹配没有随请求发送;请求带了 Cookie,但 Session 已过期;Session 有效,但资源授权失败;访问令牌有效,但刷新令牌已经轮换。用户看到的都是“登录后还是不能用”,服务端日志却要区分这些阶段。

调试时先看响应里的 Set-Cookie,再看下一次请求是否真的带上 Cookie,最后看服务端解析出的会话和权限。只在前端 console 里打印“token 存在”,无法证明认证链路完整。

轮换和撤销的代价

无状态 Token 的优点是服务端验证快、跨服务方便;缺点是签发后很难立即撤销。加入黑名单或会话版本后,服务端又需要状态查询。Session 的优点是撤销直接,缺点是每次请求都要访问共享存储,并处理高可用和过期清理。

这不是哪种方案“更先进”的问题,而是撤销速度、请求量、跨服务范围和故障恢复之间的取舍。高风险后台可能宁愿保留更多服务端状态,低风险公开接口则可能接受短时 Token。

权限变化要能传播

管理员收回权限、用户修改密码、账号被封禁时,已经签发的访问凭证不会自动知道这些变化。可以使用短过期时间、权限版本、集中会话检查或主动推送失效事件。过期时间越长,体验越顺滑,但权限变化的生效窗口也越长。

权限判断还要放在资源动作附近。先在网关判断“已登录”,再在业务服务判断“能否修改这条记录”,比只在入口做一次粗粒度判断更可靠。微服务之间传递用户身份时,也要防止下游服务把未经验证的 Header 当成可信身份。

把认证流程画成状态机

一个用户可能经历匿名、登录中、已登录、访问令牌过期、刷新中、刷新失败、主动退出和被动撤销等状态。页面如果只有 user !== null 一个判断,就无法表达刷新期间是否应该等待、失败后是否保留本地草稿、退出后是否清理缓存。

服务端也有对应状态:凭证不存在、格式错误、签名无效、已过期、已撤销、身份有效但权限不足。把这些状态合并成一个 401,客户端会不断刷新无效凭证;把它们全部返回细节,又可能泄露攻击者需要的信息。对外信息要克制,对内日志要足够定位。

认证和授权还要考虑重放。攻击者不需要破解 Token,只要在有效期内复制并重放一个合法请求,就可能完成原用户允许的动作。短过期、TLS、防重放 nonce、幂等键、设备绑定和敏感操作二次验证,可以降低不同类型的重放风险,但每种措施都有用户体验和实现成本。

最终,登录系统的可靠性取决于失效路径是否清楚:凭证过期能否恢复,刷新失败能否回到登录,撤销是否能传播到其他设备,服务器异常时是否会误删本地状态。把成功路径写通只是开始,真正考验设计的是这些不顺利的路径。

认证方案最终要服务于两个目标:让合法用户尽量少被打断,让失效或被盗的凭证尽快失去价值。前者需要刷新、持久化和多端同步,后者需要过期、撤销、轮换和审计。安全和体验不是两个互不相干的开关,而是一组需要持续调整的时间窗口。

容易踩的坑

JWT 不等于“服务端完全无状态”:撤销、刷新令牌和设备管理通常仍要服务端记一笔。Token 放 localStorage 方便,但要把 XSS 面、刷新策略和跨标签页共享一起评估;HttpOnly Cookie 另有 CSRF 与 SameSite 代价。登录成功后前端展示的权限只是 UI 提示,真正授权必须在服务端再校验一次。