前言
常见的解决方案有以下几种
- session
- jwt
- 自定义 token
- spring session
session
优势
开发方便快捷
session 是可控制的
刷新机制简单友好
缺陷
集群较复杂
由于是 session 是存储在单机的, 当横向扩展成多台服务器时, 很麻烦, 可以存储到 redis 或者在多台服务器间复制
cookie + session 在跨域场景表现并不好
session 机制可以不使用 cookie, url 重写也可以, 但大多数实现都用 cookie
服务器重启, 需要重新登录
jwt
优势
自包含, 服务器可以不存储会话信息
jwt 最大的优势在于, 服务端完全不需要存储会话信息
缺陷
刷新机制复杂
jwt 标准并没有包括刷新机制, jwt 要做刷新, 比较复杂
扩展下 如何实现 jwt 的刷新机制
首先是刷新的两个个条件: 一个 token 至多刷新一次(token 不能无限刷新); 刷新时间也有期限(比如token 过期一周内还可以刷新, 过期一周后就不能刷新了)
第一个问题: 客户端主动刷新还是服务端主动刷新
客户端, 服务端不太好主动刷新, 因为服务端不知道哪些 token 还在用,
所以需要客户端主动发刷新请求;那客户端怎么知道要发刷新请求呢?在用token的请求的时候, 如果该 token 过期, 服务端会告诉客户端, 这个token已经过期了, 这个时候客户端就知道 token 已经过期了, 需要发刷新请求了
第二个问题: token 失效前刷新还是失效后刷新
失效后; 若是失效前, 至少客户端得做一个定时刷新的机制, 麻烦
第二个问题: 如何保证 一个 token 至多刷新一次
黑名单, 已经刷新了的进黑名单(这个黑名单得存储起来了)
第三个问题: 并发问题, 当 token 过期后, 客户端并发发了几个请求, 如果所有请求都响应 token 已过期, 则客户端会继续发几个刷新请求, 当然只有一个刷新请求成功了, 其他请求都失败了, 那么最后只有一个请求成功发起了最终的请求
这个可以用宽限时间来处理
这个后面再单独写 jwt 吧
无法管理登录用户状态
由于没有存储 token, 当需要对登录用户进行状态管理时, 无能为力
eg: 管理员踢掉某个用户的登录
eg: 限制用户登录的个数, 一个用户只能在移动端 / pc 端 登录其中一个
自定义 token
自定义 token 的最大目的, 是为了解决 session 的一些缺陷, 相当于把 session 换一种方式来实现, 至少需要做如下几件事:
- 存储机制
- 刷新机制
spring session
spring session 的方式类似于自定义 token, 但多了一些其他的功能, 既然有轮子了还是不要重复造轮子的好
其他
客户端存储
强烈建议存储到 cookie 中
- 为了避免XSS,就必须用HttpOnly
使用 https
发请求时, 放 cookie 还是 header ?
- cookie 对于跨域支持不太友好
- 有些 Native App不直接支持Cookie
虽然如此, 但是由于认证信息存在 httponly 的 cookie, 无法读出来放到 header 中, 故还是通过 cookie 传