如何保存登录态:Session + Cookie vs JWT
如何保存登陆态
由于 HTTP 是无状态的,导致上一次请求和下一次请求之间没有关联性,服务器无法自动识别用户身份或请求的上下文信息。
两种常见方案
为了实现登录状态的保存,通常有两种方案:
- 服务端存储:
session + cookie - 客户端存储:
JWT token
两种方案各有优劣势
Session + Cookie
原理
给 HTTP 添加状态:给每个请求打一个标记,然后在服务端存储这个标记所对应的数据。这样每个请求就可以找到对应的数据,也就可以实现登录、权限等状态的存储。
HTTP 设置了 Cookie 机制,对应服务端的 Session,这个标记就是 session.id。
存在的问题
1. CSRF(跨站请求伪造)
由于 Cookie 在请求时会自动带上,当用户在一个网站登录后,访问别的网站时,如果该网站发起了对原网站的请求,Cookie 仍然会被带上。这时就不需要再次登录就能完成操作,万一再做些危险的操作,就会造成安全隐患。
通常利用 CSRF 漏洞的网站都是钓鱼网站,它们会伪装得很好,让人看不出破绽。
解决方案:
- 验证 Referer:检查请求是由哪个网站发起的。但这不能完全解决问题,因为 Referer 也是可以伪造的。
- 使用随机 Token:每次随机生成一个值返回给客户端,后续请求必须包含这个值。这个随机值叫做
token,可以放在header中,也可以放在参数中。因为钓鱼网站拿不到这个随机值,所以无法发起有效请求。
2. 分布式 Session
Session 是把状态保存在服务端,那如果有多台服务器呢?
解决方案:
- Session 复制:在各台机器间自动复制 Session,每次修改都同步一下。例如 Java 的
spring-session。 - 使用 Redis 存储:将 Session 保存到 Redis,这样其他服务器也能查到 Session 数据。
3. 跨域
Session + Cookie 还有跨域的问题。
Cookie 为了安全,做了 domain 的限制:设置时会指定一个 domain,只有对应 domain 的请求才会带上 Cookie。如果是跨域请求,要如何携带
Cookie 呢?
解决方案:
- 客户端手动设置
withCredentials为true - 服务端设置对应的 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 中不应保存太多数据。