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 带回服务端。
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 主要有两个来源。
1. 服务端通过 Set-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 + Session 的优点
- 浏览器自动保存和携带 Cookie
- 可以设置
HttpOnly,降低 JS 读取凭证的风险 - 服务端可以主动让 Session 失效
- 适合传统 Web 应用和同站点后台系统
Cookie + Session 的缺点
- 服务端需要存储 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。
而是可能有多种组合。
十三、常见组合方式
1. Cookie + Session
这是传统方案。
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。
3. Cookie + 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 的重要安全属性。