关于 token 过期的疑惑,为什么需要 refresh token?

2021-12-08 18:00:08 +08:00
 nianyu

平时做项目早就习惯了,在拦截器中将 access token 放入 request header 头中,如果 token 过期,则请求 refresh token 接口,拿到一个新 token ( babalabala ,还有很多细节 isRereshing and 刷新过程中将新请求的 request 放入队列...)

但是一直不是很明白为什么需要 refresh token

疑问 1: 为什么不能将 token 设置的有效期长一些,失效就直接重新登陆,而需要不停的刷新 token, 毕竟 refresh token 过期的时候,总还是要重新登陆的。

在网上搜了相关问答,有人说用户不能正在进行操作时,突然因为 token 过期重新登陆了了,然而当 refresh 也过期的时候,依然要重新登陆。

疑问 2: 有人说是为了安全问题,如果 token 被人劫持,就可以冒充用户进行一些操作,所以需要将 token 的有效期设置的非常短,这样即使破译了 token ,没过几分钟就失效了,我觉得没有道理啊,客户端通常将 access token 喝 refresh token 存在 storage 中,或者说既然能拿到 token 为什么拿不到 access token 。

12156 次点击
所在节点    信息安全
42 条回复
markgor
2021-12-08 18:20:47 +08:00
兼容场景比较大的时候,就能体现出 refreshToken 的意义;
比方第三方服务商,简称 A ;
自己的后台,简称 B ;
自己的前端,简称 C ;

疑问 1:
参考 JWT ,token 中可能包含着用户信息,token 有效期过了但 refreshToken 的没过,此时只需要通过 refreshToken 就能更新到用户信息了,对用户无感知。
如果没有 refreshToken ,则需要用户重新登录进行验证,这个时候会产生交互操作。

>然而当 refresh 也过期的时候,依然要重新登陆。
正常情况下通过 refreshToken 刷新,refreshToken 会返回一个新的,过期时间上自然是会延迟了,所以不会存在操作中需要重新登录。

*如果是说把用户 账号密码存起来,过期后再静默进行登录达到刷新 token 的话,那其实账号密码就充当了 refreshToken 角色,但这种做法可能会导致泄露用户账号密码信息,所以需要 refreshToken 。


疑问 2:
当 C 需要调用 A 的服务,会携带 token 发去 B ,由 B 发去 A ;
此时如果 C 发送的 token 超时,B 会通过 refreshToken 去刷新 token 的时长,然后重发请求给 A ,再返回结果给 C ;
这种场景中 refreshToken 并不传给 C ,而是 B 自己保存着。


以上是我自己对于 token 和 refreshToken 的见解,不过如果 token 是我自己产生的话,我会采取单一 token 形式进行,因为我自身业务可以不用考虑上面的场景,但 oauth 的设计考虑到的场景比较多,所以才会这样。
golangLover
2021-12-08 18:25:55 +08:00
refresh token 是本地储存的,发出去的是 accesstoken. 所以 access token 容易被通讯拦截。
ikas
2021-12-08 18:29:49 +08:00
对的..就是不安全...
假设只有客户端与 server
refreshToken 只是减少了传输时被偷的可能,因为大部分时间走 access token..当然因为 refreshToken 交换的时机少,你可以加入一些其他的判断..但是真的你的传输通道被监听...也没多大用
heeeeeem
2021-12-08 18:37:29 +08:00
https://www.v2ex.com/t/462507
这有个相关的贴
除去复杂的业务场景
一般就是怕只有一个 access token 在传来传去的 传输 过程中不安全,
但要是客户端被黑,那搞几个 token 都没用
我觉得 在传输过程中的安全可以由 https 保证,一般业务用一个 token 也无妨
oott123
2021-12-08 20:04:14 +08:00
accessToken 可以是 stateless 的,比如 JWT ,不做吊销,用的时候直接空手验证,不需要访问数据库
refreshToken 存到数据库里,做吊销
nianyu
2021-12-08 20:43:16 +08:00
@markgor @ikas 感谢
@heeeeeem 好的,我去看看
Rocketer
2021-12-08 22:36:43 +08:00
5 楼正解。简单来说就是 access token 负责效率,不查验状态直接用。而 refresh token 负责安全,每次使用都要查数据库,从而实现服务端注销等操作。
xtinput
2021-12-08 22:55:09 +08:00
我上家公司后台就是 refreshToken+accessToken ,refreshToken 用的时候是放 body 里面,rsa 加蜜传给后台的,accessToken 是放 header 里不加密传递(效率),然后 accessToken 有效期 2 小时,refreshToken 是 7 天。rsa 公钥是登录的时候后台交给客户端的,这个公钥是一个 rsa 密钥库里面随机拿的(有好几千个),客户端只将 refreshToken 存在应用配置信息里,accessToken 是内存缓存。部分接口 body 数据 rsa 加密,header 里包含了 rsa 公钥的编号,这个编号不是内部人士还真不知道,这样就确保安全系数不低了,再加上 https
Rocketer
2021-12-09 02:32:36 +08:00
@xtinput 其实没必要那么复杂,refresh token 更像传统意义上的 session id ,能拿到这个信息的人,基本用不到这个信息就可以直接干坏事了。

现代架构之所以用 token ,是因为后端普遍采用分布式,各服务器之间同步状态(比如 session )的开销很大,所以干脆不用状态,而是给个 token ,后端各自验证 token 的有效性而无需与其他服务器沟通,这就是所谓的 stateless 。

stateless 在绝大多数时候都没问题,但我们却不太可能实现彻底的无状态。比如用户修改了密码,服务端想强制他重新登录,这就得通知各个服务器不要再接受之前的 token 了。

要解决这个问题,常见的有 3 种方法:
1 、找个地方(内存、数据库等)记录合法的 token ,每次验证 token 都查一下这个 token 是否还在。
2 、找个地方(内存、数据库等)记录 token 的黑名单,每次验证 token 都查一下这个 token 是否在黑名单里。
3 、让 token 自己失效。

前两种都是有状态的方法,仍然避免不了状态同步的瓶颈,所以我们一般采用第三种方法。

那怎么让 token 自己失效呢? token 里一般都有时间信息,所以只需把有效期设得短一点,不再更新它,它就过期失效了。

这就引出了更新的问题,怎么更新 token 呢?我们一般用另一个 token ,这就是所谓的 refresh token 。服务端收到 refresh token 以后,是要检查黑名单或者白名单的,所以更新 token 这一步是有状态的。只有 refresh token 有效,才会下发 access token ,这样就把对状态同步的需求限制到了一个很小的范围内,从而降低状态同步成本。

自此,有状态和无状态实现了有机结合,在一起过上了幸福快乐的日子。
VeryZero
2021-12-09 09:10:02 +08:00
@Rocketer 那 refresh token 什么时候校验呢?是每次请求都校验还是 access token 过期了再校验?

如果是前者的话,相对于把 access token 放缓存里有什么区别?如果是后者,需要等几个小时才注销怕是不合适吧。。

如果只是为了做注销,完全可以把 access token 放缓存里,如果担心性能可以降低读取缓存的频率。比如每 10 分钟校验一次,其他时候都是无状态模式
VeryZero
2021-12-09 09:11:18 +08:00
我其实也一直疑惑 refresh token 的意义,楼上大佬们的解释感觉不太让人信服,期待更多信息
VeryZero
2021-12-09 09:21:38 +08:00
另外我默认咱们讨论是是自己实现的用户登录功能的 refresh token ,而不是 OAuth2 中的。后者 refresh token 确实有存在的必要性。
MX123
2021-12-09 09:29:01 +08:00
@Rocketer 你这意思 refresh token 主要是解决状态同步问题,而不是安全问题是吗?
vicalloy
2021-12-09 09:39:56 +08:00
@VeryZero refresh token 要到数据库校验的。
1. 在到期前,token 无法失效。如果你的 token 是永久的,导致这个用户及时修改密码,token 被盗,这个 token 依旧有效。
2. 如果 token 有效期比较短,比如 1 天。被盗的这个 token 在有效期内可以被正常使用。
3. 等到被盗的 token 失效后,“小偷”无法继续使用你的 token 做坏事。
wanguorui123
2021-12-09 09:40:14 +08:00
其实有 Redis 后 refreshToken 没什么用
timethinker
2021-12-09 09:54:59 +08:00
如果你的 AccessToken 不是 Stateless 的(意味着每次都要读取状态,校验 AccessToken 的合法性,判断这个 AccessToken 是否已经被撤销,或者是否已经被替换),那么 RefreshToken 就没有太大的意义。

如果你的 AccessToken 不需要读取状态(无论是数据库或者缓存),仅凭 Token 本身的签名信息就能确定它的合法性(如 JWT ),那么 RefreshToken 的存在就相当于有了一个检查点,可以在检查点确认是否还可以续签。

因此 AccessToken 的有效期应当尽量设置短一点,通过 AccessToken 访问,只要通过签名校验合法即可通行,无序读取额外的状态来进一步确认是否撤销,当 AccessToken 过期以后再通过 RefreshToken 读取额外的状态(数据库 /缓存)确认是否继续签发。
cenbiq
2021-12-09 09:58:46 +08:00
wanguorui123
2021-12-09 10:01:02 +08:00
@cenbiq accessToken 充当 SessionID
cenbiq
2021-12-09 10:03:55 +08:00
@VeryZero 流程是这样的:1.登录成功获得 refresh token 并持久化 -> 2.通过 refresh token 请求刷新得到 access token 并临时储存 -> 3.请求业务接口使用 access token -> 4. access token 过期或者快过期再次回到「 2 」 -> 5.refresh token 也过期则生命周期结束,需重新登录。
VeryZero
2021-12-09 10:13:29 +08:00
如果我没理解错,refresh token 主要用途是降低 access token 被盗后的损失。
之前我还以为每次请求都要带上 access token 和 refresh token ,那这样就没有意义了。如果只携带 access token 就能说通了。
其他功能,类似于注销踢人、自动续期等等都是附加特性,只靠 access token 也可以实现。
但是如果只依赖 access token 实现自动续期,一旦 access token 被盗,服务端是无法感知的,还是会一直帮「盗号者」续期 token 。refresh token 可以一定程度上缓解这个问题

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/820927

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX