Cookie 和 Token,到底谁在负责登录态

前言

前面几篇文章,我们已经把 HTTP 的主线铺起来了:

text 复制代码
HTTP 定义浏览器和服务器如何通信;
Header 承载 HTTP 的扩展能力;
缓存通过 Header 决定资源能不能复用。

这一篇继续沿着 Header 往下讲另一个高频问题:

text 复制代码
登录态。

在前端项目里,你一定见过这些东西:

http 复制代码
Cookie: sessionId=abc123
Authorization: Bearer xxx.yyy.zzz
Set-Cookie: sessionId=abc123; HttpOnly; Secure

也一定听过这些问题:

  • Cookie 是什么?
  • Token 是什么?
  • Session 是什么?
  • JWT 是什么?
  • Cookie 和 Token 有什么区别?
  • 为什么 HTTP 是无状态的?
  • 为什么登录后刷新页面还是登录状态?
  • token 应该放 localStorage 还是 Cookie?
  • Cookie 为什么容易和 CSRF 扯上关系?

很多同学会把 Cookie 和 Token 当成两个并列的鉴权方案:

text 复制代码
Cookie 鉴权 vs Token 鉴权

这个说法在日常沟通里没问题,但如果想真正理解它们,最好再精确一点:

text 复制代码
Cookie 是浏览器提供的一种存储和自动携带机制;
Token 是服务端签发给客户端的一种身份凭证;
Authorization 是 HTTP 请求头里常用来携带 Token 的字段。

所以 Cookie 和 Token 不是完全同一维度的东西。

这篇文章就从 HTTP 无状态开始,把 Cookie、Session、Token、JWT 这条线讲清楚。

一、为什么需要登录态

先想一个最普通的场景。

用户登录:

http 复制代码
POST /api/login HTTP/1.1
Content-Type: application/json

{
  "username": "alice",
  "password": "123456"
}

后端校验成功,返回:

json 复制代码
{
  "code": 0,
  "data": true,
  "message": "登录成功"
}

接下来用户访问个人信息:

http 复制代码
GET /api/user/current HTTP/1.1

问题来了:

text 复制代码
后端怎么知道这次请求是谁发的?
后端怎么知道这个用户刚刚登录过?
后端怎么知道应该返回 Alice 的用户信息,而不是 Bob 的?

HTTP 协议本身不会自动记住这些信息。

所以 Web 应用需要一套登录态机制。

它要解决的问题是:

text 复制代码
用户登录成功后,后续请求如何证明"我是刚刚登录过的那个用户"。

这就是 Cookie、Session、Token 这些概念出现的原因。

二、HTTP 为什么说是无状态的

HTTP 是无状态的,意思是:

text 复制代码
HTTP 协议本身不会记录上一次请求的状态。

比如先请求登录:

http 复制代码
POST /api/login

再请求当前用户:

http 复制代码
GET /api/user/current

从 HTTP 协议本身来看,这是两次独立请求。

协议并不会自动知道:

text 复制代码
这两个请求来自同一个用户;
这个用户刚刚登录成功;
这次请求应该拥有登录后的权限。

所以必须额外带上一些身份凭证。

比如:

http 复制代码
Cookie: sessionId=abc123

或者:

http 复制代码
Authorization: Bearer xxx.yyy.zzz

服务端拿到这些凭证后,才能识别:

text 复制代码
当前请求属于哪个用户;
用户有没有登录;
用户有没有权限访问这个接口。

所以可以先记住:

text 复制代码
HTTP 无状态,所以需要 Cookie、Session、Token 等机制来维护登录态。

三、Cookie 是什么

Cookie 是浏览器提供的一种机制。

它允许服务器把一小段数据保存到浏览器里。

之后浏览器请求符合条件的地址时,会自动把这段数据带回服务器。

服务端设置 Cookie,靠的是响应头:

http 复制代码
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax

浏览器后续携带 Cookie,靠的是请求头:

http 复制代码
Cookie: sessionId=abc123

所以 Cookie 相关最重要的两个 Header 是:

text 复制代码
Set-Cookie:响应头,服务端让浏览器保存 Cookie。
Cookie:请求头,浏览器把保存的 Cookie 带回服务端。

假设用户登录成功。

服务端返回:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: sessionId=abc123; Path=/; HttpOnly

{
  "code": 0,
  "data": true,
  "message": "登录成功"
}

浏览器看到:

http 复制代码
Set-Cookie: sessionId=abc123; Path=/; HttpOnly

就会把这个 Cookie 保存下来。

之后请求同一个站点:

http 复制代码
GET /api/user/current HTTP/1.1
Host: example.com
Cookie: sessionId=abc123

后端拿到 sessionId,就可以识别当前用户。

这个过程里,前端代码可能根本没有手动设置 Cookie。

例如:

js 复制代码
fetch('/api/user/current')

如果是同源请求,并且 Cookie 符合发送条件,浏览器会自动带上。

这就是 Cookie 最重要的特点:

text 复制代码
Cookie 由浏览器保存,并在符合条件时自动携带。

四、Cookie 从哪里来

Cookie 主要有两个来源。

最常见的是服务端设置:

http 复制代码
Set-Cookie: sessionId=abc123; Path=/; HttpOnly

这通常发生在:

  • 登录成功
  • 刷新登录态

比如登录成功后,后端让浏览器保存 sessionId

五、Cookie 常见属性

服务端设置 Cookie 时,通常不只是设置一个键值。

它还会带上一些属性:

http 复制代码
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=7200

常见属性如下:

属性 作用
Name=Value Cookie 的键值
Domain Cookie 可以发送到哪些域名
Path Cookie 可以发送到哪些路径
Expires 过期时间
Max-Age 多少秒后过期
HttpOnly JS 不能读取
Secure 只在 HTTPS 下发送
SameSite 限制跨站请求携带 Cookie

六、Session 是什么

讲 Cookie 时,经常会一起讲 Session。

Session 是服务端用来保存用户登录状态的一种机制。

可以这样理解:

text 复制代码
Cookie 存在浏览器;
Session 存在服务端。

用户登录成功后,服务端创建一份 Session 数据:

text 复制代码
sessionId=abc123 -> userId=1001

服务端再把 sessionId 通过 Cookie 发给浏览器:

http 复制代码
Set-Cookie: sessionId=abc123; HttpOnly

后续浏览器请求时带上:

http 复制代码
Cookie: sessionId=abc123

服务端根据 sessionId 查 Session:

text 复制代码
abc123 对应 userId=1001

于是服务端知道:

text 复制代码
当前请求来自用户 1001。

七、Cookie + Session 登录流程

完整流程如下:

text 复制代码
1. 用户提交账号密码。
2. 服务端校验成功。
3. 服务端创建 Session,并生成 sessionId。
4. 服务端通过 Set-Cookie 把 sessionId 返回给浏览器。
5. 浏览器保存 Cookie。
6. 后续请求浏览器自动携带 Cookie。
7. 服务端根据 sessionId 查询 Session,识别当前用户。

示例:

http 复制代码
POST /api/login HTTP/1.1
Content-Type: application/json

{
  "username": "alice",
  "password": "123456"
}

服务端返回:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: JSESSIONID=abc123; Path=/; HttpOnly; Secure

{
  "code": 0,
  "data": true,
  "message": "登录成功"
}

之后请求当前用户:

http 复制代码
GET /api/user/current HTTP/1.1
Host: example.com
Cookie: JSESSIONID=abc123

服务端拿到 JSESSIONID,找到对应 Session,返回用户信息。

  • 浏览器自动保存和携带 Cookie
  • 可以设置 HttpOnly,降低 JS 读取凭证的风险
  • 服务端可以主动让 Session 失效
  • 适合传统 Web 应用和同站点后台系统
  • 服务端需要存储 Session
  • 分布式系统要考虑 Session 共享
  • 跨域携带 Cookie 配置比较麻烦
  • 因为 Cookie 会自动携带,所以要注意 CSRF 风险

八、Token 是什么

Token 可以理解为服务端签发给客户端的一种身份凭证。

登录成功后,后端返回一个 token:

json 复制代码
{
  "code": 0,
  "data": {
    "token": "xxx.yyy.zzz"
  },
  "message": "登录成功"
}

前端保存 token。

后续请求时,把 token 带给服务端。

常见方式是放在请求头:

http 复制代码
Authorization: Bearer xxx.yyy.zzz

服务端收到后校验 token。

如果合法,就认为用户已登录。

所以 Token 的核心是:

text 复制代码
客户端每次请求时携带一段身份凭证;
服务端通过校验这段凭证识别用户。

九、Authorization: Bearer token 是什么

Authorization 是 HTTP 请求头,专门用于携带认证信息。

常见写法:

http 复制代码
Authorization: Bearer xxx.yyy.zzz

这里的 Bearer 可以理解为一种认证方案。

它的意思大概是:

text 复制代码
谁持有这个 token,谁就拥有对应身份。

前端请求示例:

js 复制代码
const token = localStorage.getItem('token')

fetch('/api/user/current', {
  headers: {
    Authorization: `Bearer ${token}`
  }
})

axios 里通常会用请求拦截器统一添加:

js 复制代码
axios.interceptors.request.use((config) => {
  const token = localStorage.getItem('token')

  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }

  return config
})

所以:

text 复制代码
Token 是身份凭证;
Authorization 是常见的传递位置;
Bearer 是常见的 token 认证格式。

十、Token 登录流程

Token 登录的一般流程是:

text 复制代码
1. 用户提交账号密码。
2. 服务端校验成功。
3. 服务端生成 token。
4. 服务端把 token 放在响应体里返回给前端。
5. 前端保存 token。
6. 后续请求前端主动把 token 放进 Authorization 请求头。
7. 服务端校验 token,识别当前用户。

示例:

http 复制代码
POST /api/login HTTP/1.1
Content-Type: application/json

{
  "username": "alice",
  "password": "123456"
}

服务端返回:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json

{
  "code": 0,
  "data": {
    "token": "xxx.yyy.zzz"
  },
  "message": "登录成功"
}

前端保存:

js 复制代码
localStorage.setItem('token', 'xxx.yyy.zzz')

后续请求:

http 复制代码
GET /api/user/current HTTP/1.1
Host: example.com
Authorization: Bearer xxx.yyy.zzz

服务端校验 token 后返回当前用户。

Token 方案的优点

  • 适合前后端分离项目
  • 适合 App、小程序、第三方 API
  • 不强依赖浏览器 Cookie 机制
  • 跨端使用比较灵活
  • 服务端可以设计成相对无状态

Token 方案的缺点

  • 前端需要自己保存和携带 token
  • token 存在 localStorage 时容易被 XSS 读取
  • token 过期、刷新、吊销需要额外设计
  • token 一旦泄露,在过期前可能被冒用

十一、JWT 是什么

JWT,全称是 JSON Web Token。

它是一种常见的 token 格式。

JWT 通常长这样:

text 复制代码
xxxxx.yyyyy.zzzzz

它由三部分组成:

text 复制代码
Header.Payload.Signature

1. Header

Header 通常描述 token 类型和签名算法。

例如:

json 复制代码
{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload

Payload 用来存放声明信息。

例如:

json 复制代码
{
  "userId": 1001,
  "username": "alice",
  "exp": 1790000000
}

注意:

text 复制代码
JWT 的 Payload 默认只是编码,不是加密。

所以不要把密码、身份证号、银行卡号等敏感信息放进去。

3. Signature

Signature 是签名,用来防止 token 被篡改。

服务端可以通过签名判断:

text 复制代码
这个 token 是否由我签发;
中间内容有没有被改过。

JWT 的特点

JWT 的优点:

  • 自包含,可以携带用户信息
  • 服务端可以通过签名验证合法性
  • 适合分布式系统
  • 不一定需要每次查 Session

JWT 的问题:

  • Payload 默认不加密
  • token 体积可能较大
  • 一旦签发,过期前不容易主动失效
  • 吊销、续期、刷新机制需要额外设计

可以这样理解:

text 复制代码
JWT 是 Token 的一种常见格式,但 Token 不一定都是 JWT。

十二、Cookie 和 Token 到底有什么区别

这一节是全文重点。

很多人会问:

text 复制代码
Cookie 和 Token 有什么区别?

但严格说,它们不是完全同一类东西。

更准确的说法是:

text 复制代码
Cookie 是浏览器的存储和自动携带机制;
Token 是身份凭证;
Authorization 是常见的 token 传递请求头。

对比一下:

对比点 Cookie Token
本质 浏览器存储和自动携带机制 身份凭证
常见位置 Cookie 请求头 Authorization 请求头或其他位置
来源 服务端 Set-Cookie 或前端 document.cookie 登录接口返回,服务端签发
携带方式 浏览器自动携带 前端代码主动携带
常见方案 Cookie + Session Authorization + Token/JWT
主要安全风险 CSRF 风险更典型 XSS 窃取风险更典型

所以日常可以说:

text 复制代码
Cookie 鉴权和 Token 鉴权。

但理解时要知道:

text 复制代码
Cookie 是一种携带凭证的机制;
Token 是被携带的凭证。

甚至 token 也可以放在 Cookie 里。

例如:

http 复制代码
Set-Cookie: token=xxx.yyy.zzz; HttpOnly; Secure

所以不是:

text 复制代码
用了 Cookie 就不能用 Token。

而是可能有多种组合。

十三、常见组合方式

这是传统方案。

text 复制代码
Cookie 里放 sessionId;
Session 数据存在服务端;
后端通过 sessionId 找用户。

请求里:

http 复制代码
Cookie: JSESSIONID=abc123

服务端查:

text 复制代码
abc123 -> userId=1001

2. Authorization + Token

这是前后端分离里很常见的方案。

text 复制代码
登录后后端返回 token;
前端保存 token;
请求时放到 Authorization。

请求里:

http 复制代码
Authorization: Bearer xxx.yyy.zzz

服务端校验 token。

也可以把 token 放在 Cookie 中。

例如:

http 复制代码
Set-Cookie: access_token=xxx.yyy.zzz; HttpOnly; Secure; SameSite=Lax

这样浏览器会自动携带 token:

http 复制代码
Cookie: access_token=xxx.yyy.zzz

优点是:

text 复制代码
如果设置 HttpOnly,JS 不能读取 token。

但也要注意:

text 复制代码
浏览器自动携带 Cookie,所以要考虑 CSRF。

十四、面试怎么回答

如果面试官问:

text 复制代码
Cookie 和 Token 有什么区别?

可以这样答:

text 复制代码
Cookie 是浏览器提供的一种存储和自动携带机制,服务端可以通过 Set-Cookie 把 sessionId 等信息写到浏览器,后续请求浏览器会在符合条件时自动带上 Cookie。

Token 是服务端签发给客户端的身份凭证,登录成功后前端拿到 token,通常会保存起来,并在后续请求中通过 Authorization: Bearer token 主动携带给服务端。

严格说 Cookie 和 Token 不是完全同一维度的东西,Cookie 是携带机制,Token 是身份凭证。常见方案有 Cookie + Session,也有 Authorization + Token,甚至也可以把 token 放在 HttpOnly Cookie 中。

如果追问:

text 复制代码
Cookie + Session 和 Token 方案有什么区别?

可以答:

text 复制代码
Cookie + Session 中,服务端保存 Session,浏览器通过 Cookie 携带 sessionId,服务端根据 sessionId 找用户。它的优点是浏览器自动携带,服务端可以主动控制 Session 失效,但服务端需要存储 Session,分布式时要考虑共享,且 Cookie 自动携带会带来 CSRF 风险。

Token 方案中,服务端签发 token,前端保存后通过 Authorization 请求头主动携带。它更适合前后端分离、App、小程序和开放 API,但 token 存在 localStorage 时容易被 XSS 读取,过期、刷新和吊销也需要额外设计。

十五、本篇小结

这一篇我们围绕 Header 里的登录态问题,讲了 Cookie、Session、Token 和 JWT。

核心可以总结成几句话:

text 复制代码
HTTP 是无状态的,所以需要额外机制识别用户身份。
Cookie 是浏览器的存储和自动携带机制。
Set-Cookie 是响应头,Cookie 是请求头。
Session 通常存在服务端,Cookie 中常放 sessionId。
Token 是服务端签发给客户端的身份凭证。
Authorization 是常见的 token 传递请求头。
JWT 是 Token 的一种常见格式。

一定要记住这句:

text 复制代码
Cookie 是机制,Token 是凭证,Authorization 是位置。

常见方案有:

text 复制代码
Cookie + Session:浏览器自动带 sessionId,服务端查 Session。
Authorization + Token:前端主动带 token,服务端校验 token。
Cookie + Token:token 放在 Cookie 中,常配合 HttpOnly。

安全上要知道:

text 复制代码
localStorage token 重点防 XSS;
Cookie 自动携带重点防 CSRF;
HttpOnly、Secure、SameSite 是 Cookie 的重要安全属性。
相关推荐
阳火锅1 小时前
2025,记住这一年!它是古法编程的最后一年。
前端·javascript·面试
sibylyue1 小时前
【报表和BI大屏】ECharts数据可视化大屏 / 驾驶舱
前端·信息可视化·echarts
用户921080262861 小时前
为什么 Header 是 HTTP 的灵魂
前端
特立独行的猫A1 小时前
AtomMQTT Broker — 用 Rust 实现的轻量级高性能 MQTT 消息代理
前端·后端·架构
IMPYLH1 小时前
HTML 的 <s> 元素
前端·html
lhldsg1 小时前
折扣卡CPS小程序定制全流程实战指南:从需求分析到上线部署
java·前端·小程序
计算机魔术师1 小时前
Lambert 算了一笔账:CUDA 绑定开源生态,每年值 100 亿美元
前端
晴天161 小时前
HTML iframe 标签全面解析:原理、属性、实战场景与最佳实践
前端·html·状态模式
parade岁月1 小时前
倒反天罡!押注 React Native 6 年后,Shopify 又回到了原生开发
android·前端·ios