2025/03/05

如何保存登录态:Session + Cookie vs JWT

HTTP 无状态问题下的登录态保存方案,对比分析 Session + Cookie 和 JWT Token 两种方案的原理、优势及存在的问题。

如何保存登陆态

由于 HTTP 是无状态的,导致上一次请求和下一次请求之间没有关联性,服务器无法自动识别用户身份或请求的上下文信息。

两种常见方案

为了实现登录状态的保存,通常有两种方案:

  • 服务端存储session + cookie
  • 客户端存储JWT token

两种方案各有优劣势

原理

HTTP 添加状态:给每个请求打一个标记,然后在服务端存储这个标记所对应的数据。这样每个请求就可以找到对应的数据,也就可以实现登录、权限等状态的存储。

HTTP 设置了 Cookie 机制,对应服务端的 Session,这个标记就是 session.id

存在的问题

1. CSRF(跨站请求伪造)

由于 Cookie 在请求时会自动带上,当用户在一个网站登录后,访问别的网站时,如果该网站发起了对原网站的请求,Cookie 仍然会被带上。这时就不需要再次登录就能完成操作,万一再做些危险的操作,就会造成安全隐患。

通常利用 CSRF 漏洞的网站都是钓鱼网站,它们会伪装得很好,让人看不出破绽。

解决方案:

  1. 验证 Referer:检查请求是由哪个网站发起的。但这不能完全解决问题,因为 Referer 也是可以伪造的。
  2. 使用随机 Token:每次随机生成一个值返回给客户端,后续请求必须包含这个值。这个随机值叫做 token,可以放在 header 中,也可以放在参数中。因为钓鱼网站拿不到这个随机值,所以无法发起有效请求。

2. 分布式 Session

Session 是把状态保存在服务端,那如果有多台服务器呢?

解决方案:

  1. Session 复制:在各台机器间自动复制 Session,每次修改都同步一下。例如 Java 的 spring-session
  2. 使用 Redis 存储:将 Session 保存到 Redis,这样其他服务器也能查到 Session 数据。

3. 跨域

Session + Cookie 还有跨域的问题。

Cookie 为了安全,做了 domain 的限制:设置时会指定一个 domain,只有对应 domain 的请求才会带上 Cookie。如果是跨域请求,要如何携带 Cookie 呢?

解决方案:

  1. 客户端手动设置 withCredentialstrue
  2. 服务端设置对应的 header
Access-Control-Allow-Origin: 当前域名
Access-Control-Allow-Credentials: true

⚠️:设置为 Allow-Origin: * 是不行的,必须指定具体的域名才能接受跨域的 Cookie。

JWT Token

Token 方案通常使用 JSON 格式保存,称为 JSON Web Token,简称 JWT。

JWT 结构

JWT 是保存在 Headers 中的一段字符串,由三部分组成:

部分说明
Header保存当前的加密算法
Payload存储具体的数据
Signature将 Header、Payload 做一次加密之后生成

优势

JWT 解决了 Session + Cookie 方案中的 CSRF、分布式 Session 和跨域等问题。

存在的问题

1. 安全性

由于 Token 直接放在 Header 中,理论上可以被伪造,所以需要搭配 HTTPS 使用,防止 Token 被拦截。

2. 性能

放在 Header 中,每次请求都会带上,请求内容变多,性能会有影响。因此 JWT 中不应保存太多数据。