Cookie-Session 认证模式与Token认证模式
Cookie-Session 认证模式与Token认证模式
我们知道HTTP协议是一种无状态,无连接的协议,即服务器不会在两个请求之间保持任何状态信息。每个请求都是独立的,服务器不能从之前的请求中获取任何信息。这导致了每个请求都必须包含所有必要的信息,每次请求都需要建立一个新的连接。这意味着每个请求都需要经历连接建立、数据传输和连接关闭等阶段。
但是随着Web应用的不断兴起,比如各种购物网站,社交网站等需要管理不同的人相关的信息,需要保存用户相关的信息,即解决“记住用户是谁”的问题,需要一种“状态保持”的机制,于是出现了Cookie以及Session
一. Cookie-Session 认证模式
简单介绍Cookie和Session的概念
Cookie是存储在客户端(用户浏览器)的小块数据,可以用来记住用户的相关信息,例如登录凭证或偏好设置。它们随每个HTTP请求发送给服务器,并且可以被服务器读取以维持会话或个性化用户体验。
**客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是Session。**客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。每个用户访问服务器都会建立一个session并自动分配一个
SessionId
,用于标识用户的唯一身份。
因为Cookie 本身只能保存少量数据,而且安全性差(明文存储、容易被窃取),为了解决 Cookie 直接存储用户敏感信息的问题,Web 开发者引入了 Session。
具体逻辑
客户端使用 用户名、密码 进行认证
服务端验证⽤用户名、密码正确后生成并存储 Session,将
SessionID
通过 Cookie 返回给客户端客户端访问需要认证的接口时在 Cookie 中携带
SessionID
服务端通过
SessionID
查找 Session 并进行鉴权,返回给客户端需要的数据
存在问题
- 因为每个人的
SessionID
都存在服务器中,对于服务器来说开销很大,且由于 Session 需要经常快速查找,通常存储在内存或内存数据库 中,同时在线⽤用户较多时需要占⽤用⼤大量量的服务器器资源
2.当需要扩展时,创建 Session 的服务器器可能不是验证 Session 的服务器器,所以还需要将所有 Session 单独存储并共享,比如我用两个机器组成了一个集群, 我通过机器A登录了系统, 那session id会保存在机器A上, 假设我的下一次请求被转发到机器但机器B没有我的 session id。
此时我就需要在两个机器之间进行Session复制
- 由于客户端使用 Cookie 存储
SessionID
,在跨域场景下需要进行兼容性处理(假如,同时这种方式也难 以防范 CSRF 攻击
兼容性处理:如果你的前端页面和后端 API 不在同一个域名下(例如
a.com
调api.b.com
),Cookie 默认不会自动发送,需要进行额外的配置,否则 Cookie 传不过去,Session 机制就失效了。
CSRF攻击:CSRF(跨站请求伪造) 利用的就是 Cookie 自动携带 的特性。
场景:
- 你登录了银行网站
bank.com
,浏览器保存了一个SessionID
Cookie。- 恶意网站
evil.com
上有一段代码发起请求POST bank.com/transfer?to=xxx&amount=1000
。- 浏览器在访问
bank.com
时会自动带上SessionID
,银行就会以为是你本人操作。这就是为什么单纯依赖 Cookie+SessionID 难以防御 CSRF,需要额外的手段,比如:
- CSRF Token(双提交、SameSite Cookie)
- 验证码
- Referer/Origin 校验
二. Token认证模式
为了解决上述的问题, 首先决定不再将SessionID保存在服务器中,可知客户端登录时请求头中会包含用户名,密码等数据,然后会通过加密算法和特定密钥对请求头的这些数据做一个签名,将签名和请求头数据整体作为一个“令牌”,这个令牌同样不会保存,而是在下一次登录时对请求头中的数据以同样的加密算法和特定密钥生成签名,然后将该签名与令牌中的签名进行对比,如果数据被篡改了,签名会改变,这样就会被拒绝登录。
这种方式即Token认证模式,是一种无状态的会话管理方式
客户端使⽤ 用户名、密码进行认证
服务端验证用户名、密码正确后生成 Token 返回给客户端
客户端保存 Token,访问需要认证的接口时在 URL 参数或 HTTP Header 中加入 Token
服务端通过解码 Token 进行鉴权,返回给客户端需要的数据
基于 Token 的会话管理方式有效解决了基于 Session 的会话管理方式带来的问题。
服务端不需要存储和用户鉴权有关的信息,鉴权信息会被加密到 Token 中,服务端只需要读取 Token 中包含的鉴权信息即可
避免了共享 Session 导致的不易扩展问题
不需要依赖 Cookie,有效避免 Cookie 带来的 CSRF 攻击问题
使用 CORS 可以快速解决跨域问题
JWT
JWT 是 JSON Web Token 的缩写,是为了在⽹网络应用环境间传递声明而执行的⼀一种基于JSON的开放标准((RFC 7519 )。JWT 本身没有定义任何技术实现,它只是定义了⼀一种基于 Token 的会话管理的规则, 涵盖 Token 需要包含的标准内容和 Token 的生成过程,特别适用于分布式站点的单点登录(SSO)场景。
//一个jwt token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VySWQiOjEyMywiZXhwIjoxNjk5MjEyMDB9
.xkTrqBMY-6ckcVJRY7QFsQFZ7VbJ1U7c_0a3lmT9YcM
它是由 . 分隔的三部分组成
header.payload.signature
//头部.负载.签名
Header(头部)
-
作用:声明这是什么类型的 Token、以及使用什么签名算法。
-
格式:一个 JSON 对象,常见字段:
{"alg": "HS256", // 签名算法 (HMAC-SHA256)"typ": "JWT" // Token 类型 }
-
编码:会被 Base64URL 编码,作为 JWT 的第一段。
例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload(负载)
- 作用:存放用户相关的数据,JWT 的主体内容。
- 里面分两类:
- 标准声明 (Registered Claims):JWT 规范推荐的字段,比如:
iss
:签发者 (issuer)sub
:主题 (subject)exp
:过期时间 (expiration)iat
:签发时间 (issued at)nbf
:生效时间 (not before)
- 自定义声明:应用自己加的,比如:
userId: 123
role: admin
- 标准声明 (Registered Claims):JWT 规范推荐的字段,比如:
- 格式:JSON 对象,也会被 Base64URL 编码,作为第二段。
例如:
{"userId": 123,"role": "admin","exp": 1699212000
}
编码后类似:
eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIiwiZXhwIjoxNjk5MjEyMDAwfQ
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。这个 JSON 对象也要使用 Base64URL 算法转成字符串。
Signature(签名)
-
作用:防止 Token 被篡改。
-
生成方式:
-
把前两部分拼接:
base64url(header) + "." + base64url(payload)
-
用密钥 + 指定算法(比如
HS256
)生成哈希签名。
-
-
例如伪代码:
signature = HMACSHA256(base64url(header) + "." + base64url(payload), secret)
-
结果再做 Base64URL 编码,作为第三段。
这样别人即使改了 payload(比如把 role 从 user 改成 admin),因为没法生成正确的签名,服务端验证时会失败。
最终拼起来:
header.payload.signature
比如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIiwiZXhwIjoxNjk5MjEyMDAwfQ
.xkTrqBMY-6ckcVJRY7QFsQFZ7VbJ1U7c_0a3lmT9YcM
JWT优缺点小结
优点
-
无状态(Stateless)
- 服务端不需要存储 Session 数据,验证时只要校验 Token 是否正确和是否过期。
- 好处:服务端压力小,适合分布式、微服务架构。
-
跨域友好
-
JWT 不依赖浏览器 Cookie,通常放在
Authorization
头里:Authorization: Bearer <token>
-
在前后端分离、移动端/小程序/第三方调用时,比 Cookie 更灵活。
-
-
扩展性强
- Payload 里可以存储用户 ID、角色、权限等信息,减少数据库查询次数。
- 比 SessionID(只存一个 ID,再查库获取数据)更直接。
-
可自包含(Self-contained)
- 一个 Token 里包含了足够的用户信息,服务端只需要验证签名,就能知道用户是谁、权限是什么。
-
多平台通用
- 只要能处理 HTTP Header,就能带 JWT,不依赖浏览器特性,适合 App、API、IoT 等多端。
缺点
- 无法主动失效
- Session 可以在服务端删除,用户下次请求就失效。
- JWT 是无状态的,一旦签发,只能等到过期时间(
exp
)才自动失效。 - 想要立即失效(比如用户退出登录),需要额外实现 黑名单/撤销列表。
- 安全风险
- 如果 JWT 被窃取(比如存储在
localStorage
被 XSS 攻击读走),攻击者就能一直冒充用户,直到 Token 过期。 - 所以 JWT 更怕 XSS,而 Session 更怕 CSRF。
- 如果 JWT 被窃取(比如存储在
- 体积大
- JWT 本身比较长(动辄几百字节),请求头每次都要带上,网络开销比 SessionID 大。
- 对频繁请求的 API 来说,会增加带宽消耗。
- 刷新机制复杂
- JWT 一般要结合 Refresh Token 使用:
- Access Token(短期有效,几分钟 ~ 几小时)。
- Refresh Token(长期有效,用来换新的 Access Token)。
- 否则要么 Token 太短导致频繁登录,要么太长导致安全隐患。
- JWT 一般要结合 Refresh Token 使用:
- 信息暴露
- Payload 是 Base64URL 编码,不是加密,任何人都能解码看到内容(虽然篡改不了)。
- 所以不能把敏感数据(密码、银行卡号)放在 JWT 里。
总结对比
方面 | Session + Cookie | JWT |
---|---|---|
存储 | 服务端存 Session | 客户端保存 Token |
跨域 | 需要 CORS 配置,SameSite Cookie 限制 | 直接放 Header,跨域无障碍 |
安全性 | 易受 CSRF 攻击 | 易受 XSS 攻击 |
失效控制 | 服务端可随时销毁 Session | 无法主动失效,需要黑名单 |
扩展性 | 存 ID,需要查数据库 | 可携带更多信息,减少查库 |
性能 | Session 存多了会占内存 | 服务端压力小,但 Token 长,占带宽 |
Session 适合单体应用,安全性强,可控性好。
JWT 适合前后端分离、分布式、移动端等场景,但要额外考虑安全与失效机制
|
| 失效控制 | 服务端可随时销毁 Session | 无法主动失效,需要黑名单 |
| 扩展性 | 存 ID,需要查数据库 | 可携带更多信息,减少查库 |
| 性能 | Session 存多了会占内存 | 服务端压力小,但 Token 长,占带宽 |
Session 适合单体应用,安全性强,可控性好。
JWT 适合前后端分离、分布式、移动端等场景,但要额外考虑安全与失效机制