平时做项目早就习惯了,在拦截器中将 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 。
1
markgor 2021-12-08 18:20:47 +08:00 3
兼容场景比较大的时候,就能体现出 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 的设计考虑到的场景比较多,所以才会这样。 |
2
golangLover 2021-12-08 18:25:55 +08:00 via Android
refresh token 是本地储存的,发出去的是 accesstoken. 所以 access token 容易被通讯拦截。
|
3
ikas 2021-12-08 18:29:49 +08:00 1
对的..就是不安全...
假设只有客户端与 server refreshToken 只是减少了传输时被偷的可能,因为大部分时间走 access token..当然因为 refreshToken 交换的时机少,你可以加入一些其他的判断..但是真的你的传输通道被监听...也没多大用 |
4
heeeeeem 2021-12-08 18:37:29 +08:00 1
https://www.v2ex.com/t/462507
这有个相关的贴 除去复杂的业务场景 一般就是怕只有一个 access token 在传来传去的 传输 过程中不安全, 但要是客户端被黑,那搞几个 token 都没用 我觉得 在传输过程中的安全可以由 https 保证,一般业务用一个 token 也无妨 |
5
oott123 2021-12-08 20:04:14 +08:00 6
accessToken 可以是 stateless 的,比如 JWT ,不做吊销,用的时候直接空手验证,不需要访问数据库
refreshToken 存到数据库里,做吊销 |
7
Rocketer 2021-12-08 22:36:43 +08:00 via iPhone
5 楼正解。简单来说就是 access token 负责效率,不查验状态直接用。而 refresh token 负责安全,每次使用都要查数据库,从而实现服务端注销等操作。
|
8
xtinput 2021-12-08 22:55:09 +08:00 1
我上家公司后台就是 refreshToken+accessToken ,refreshToken 用的时候是放 body 里面,rsa 加蜜传给后台的,accessToken 是放 header 里不加密传递(效率),然后 accessToken 有效期 2 小时,refreshToken 是 7 天。rsa 公钥是登录的时候后台交给客户端的,这个公钥是一个 rsa 密钥库里面随机拿的(有好几千个),客户端只将 refreshToken 存在应用配置信息里,accessToken 是内存缓存。部分接口 body 数据 rsa 加密,header 里包含了 rsa 公钥的编号,这个编号不是内部人士还真不知道,这样就确保安全系数不低了,再加上 https
|
9
Rocketer 2021-12-09 02:32:36 +08:00 19
@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 ,这样就把对状态同步的需求限制到了一个很小的范围内,从而降低状态同步成本。 自此,有状态和无状态实现了有机结合,在一起过上了幸福快乐的日子。 |
10
VeryZero 2021-12-09 09:10:02 +08:00
@Rocketer 那 refresh token 什么时候校验呢?是每次请求都校验还是 access token 过期了再校验?
如果是前者的话,相对于把 access token 放缓存里有什么区别?如果是后者,需要等几个小时才注销怕是不合适吧。。 如果只是为了做注销,完全可以把 access token 放缓存里,如果担心性能可以降低读取缓存的频率。比如每 10 分钟校验一次,其他时候都是无状态模式 |
11
VeryZero 2021-12-09 09:11:18 +08:00
我其实也一直疑惑 refresh token 的意义,楼上大佬们的解释感觉不太让人信服,期待更多信息
|
12
VeryZero 2021-12-09 09:21:38 +08:00
另外我默认咱们讨论是是自己实现的用户登录功能的 refresh token ,而不是 OAuth2 中的。后者 refresh token 确实有存在的必要性。
|
14
vicalloy 2021-12-09 09:39:56 +08:00 1
@VeryZero refresh token 要到数据库校验的。
1. 在到期前,token 无法失效。如果你的 token 是永久的,导致这个用户及时修改密码,token 被盗,这个 token 依旧有效。 2. 如果 token 有效期比较短,比如 1 天。被盗的这个 token 在有效期内可以被正常使用。 3. 等到被盗的 token 失效后,“小偷”无法继续使用你的 token 做坏事。 |
15
wanguorui123 2021-12-09 09:40:14 +08:00
其实有 Redis 后 refreshToken 没什么用
|
16
timethinker 2021-12-09 09:54:59 +08:00 4
如果你的 AccessToken 不是 Stateless 的(意味着每次都要读取状态,校验 AccessToken 的合法性,判断这个 AccessToken 是否已经被撤销,或者是否已经被替换),那么 RefreshToken 就没有太大的意义。
如果你的 AccessToken 不需要读取状态(无论是数据库或者缓存),仅凭 Token 本身的签名信息就能确定它的合法性(如 JWT ),那么 RefreshToken 的存在就相当于有了一个检查点,可以在检查点确认是否还可以续签。 因此 AccessToken 的有效期应当尽量设置短一点,通过 AccessToken 访问,只要通过签名校验合法即可通行,无序读取额外的状态来进一步确认是否撤销,当 AccessToken 过期以后再通过 RefreshToken 读取额外的状态(数据库 /缓存)确认是否继续签发。 |
17
cenbiq 2021-12-09 09:58:46 +08:00
@wanguorui123 why?
|
18
wanguorui123 2021-12-09 10:01:02 +08:00
@cenbiq accessToken 充当 SessionID
|
19
cenbiq 2021-12-09 10:03:55 +08:00 1
@VeryZero 流程是这样的:1.登录成功获得 refresh token 并持久化 -> 2.通过 refresh token 请求刷新得到 access token 并临时储存 -> 3.请求业务接口使用 access token -> 4. access token 过期或者快过期再次回到「 2 」 -> 5.refresh token 也过期则生命周期结束,需重新登录。
|
20
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 可以一定程度上缓解这个问题 |
21
Qsong 2021-12-09 10:14:11 +08:00 1
token 的过期时间一般会比 refeshToken 的过期时间短很多,保证 token 被盗取后无法持久的做坏事。
refeshToken 一般会存储在后端服务器,而不会放在客户端,如果说放在客户端,那 token 将毫无意义 当 token 过期后,后端会利用 refeshToken 返回一个新的 token 给到前端使用,而不是前端拿一个过期的 token 获取一个新的 token ,只要 token 过期了,那么 token 就已经是无效的了,只能用新的 token 才可以正常使用。 一般后端会将 token 放在请求头里返回给前端,前端只要拿着 header 里的 token 调用接口就行了,对于是否刷新了 token 也是无感的 |
22
dingwen07 2021-12-09 10:15:39 +08:00 via iPhone
如果用户持续地访问这个网站,他们可以一直保持登录状态,而不需要定期重新登录。
|
23
robinlovemaggie 2021-12-09 10:15:54 +08:00
token 的设计本身不负责安全性,所以劫持问题不应在 token 这里讨论。
|
24
aboat365 2021-12-09 10:16:03 +08:00 1
token 翻译成中文就是令牌。因为 HTTP1.1 协议每次请求都是独立的,不能复用的连接。所以,在每次请求中都需要带上令牌。令牌有效期越长,那么请求携带次数就越多,传输过程越容易暴露令牌,导致安全性下降。总而言之,就是一个令牌使用时间和次数越多,那么在使用过程中越不安全。反之,把令牌有效期设置的越短,那么就越安全。但如果这样子,将导致用户体验很差。那么如何解决这个矛盾的?对于经典的 web 应用,令牌的有效时间是 30 分钟,在有效期内使用令牌请求,后端将刷新令牌的有效期。如果超过 30 分钟再发起请求,服务端会要求前端带上 refresh token ,即刷新令牌。刷新令牌有效期可以设置很多天,比如设置一周。刷新令牌设置这么长时间那不是很不安全?其实不是的,刷新令牌相比前面每次请求使用的令牌来说,区别在于低频次使用。虽然有效期长得多,但使用次数非常低,有的方案只用一次。如前所述,使用刷新令牌换取普通令牌,并获取一个新的刷新令牌,原刷新令牌作废。如此,便实现了用户登录后,只要不超过一周内访问服务,那便可以一直免登录(例如你手机上的微信)。最后关于令牌本身,可以是服务端随机生成的 session id ,也可以是服务端不保存映射关系的 JWT ,主要看具体应用场景。
最后归纳一下,token 和 refresh token 的区别在于有效期一个短一个长。使用上 token 用于每次请求,refresh token 用于 token 过期后去换取新的 token 和 refresh token 。这样设计的目的,就是为了解决安全性和使用体验之间的矛盾。 |
25
libook 2021-12-09 10:29:41 +08:00 1
从需求出发:
1. 用户不希望做每个操作都进行登录验证操作,所以需要让用户会话有一定的有效时间; 2. 用户不希望在长期不做操作的情况下,比如离开电脑前,甚至电脑交给他人使用的时候,会话仍然有效,以至于可以被他人操作,所以用户会话的有效时间不能太长; 3. 用户不希望自己正在使用的时候会话突然过期,所以需要在用户会话过期之前,如果用户还有操作的话,就对会话续期(刷新)。 题主疑问 2 里面说的“有人说”的观点是错误的,安全性都是相对的,没有网络通信安全和客户端安全作为保障,token 机制不管怎么用都是无法保证安全的。 |
26
onhao 2021-12-09 10:40:19 +08:00
我只说我们的 refresh token ,这个接口就是起到延期 token 的作用,可以让用户能在一定的日期内登录始终是有效的,这样避免了 token 一直固定也可提升用户体验-无感登录
|
27
Rocketer 2021-12-09 11:11:16 +08:00 via iPhone
@VeryZero refresh token 每次使用都要校验状态。
至于什么时候拿 refresh token 去换取一个新的 access token ,那是客户端的事。它可以在 access token 即将到期时主动更新,也可以在服务端返回失效错误后再去更新,还可以在任何自己认为需要的时候去更新(比如自己刚修改了密码,于是立刻去获取个新的 access token ) |
28
neetrorschach 2021-12-09 14:05:58 +08:00 1
说下我在某外企客户那儿对接他们的 oauth2 的开发指导文档吧。
用户每次登录会获取 access token 和 refresh token 前端保存 access token 和 refresh token 每次前端请求带上 access token ,后端用 access token 去 oauth2 服务验证是否有效。 如果无效则反馈给前端,前端会再次用 refresh token 发送请求。oauth2 会验证 refresh token 是否在有效期内。如果有效,则返回新的 access token 和 refresh token (延长有效期)。前端更新存储的 token 。 如果 refresh token 无效则跳转到登录页。 refresh token 设置了一周时间。如果用户每天用,其实基本不会要求重登录。只有长期不用才会要求重新登录。 |
29
dinding 2021-12-09 14:43:01 +08:00
1 从权限上说,refresh token 是和授权服务器之间的凭证,access token 是和资源服务器之间的凭证。
2 从实现上,有效的 access token 可以有多个,但是 refresh token 只能有一个。所以前者可以实现为 self-contanined(包含了请求资源的所有信息),无需持久化存储;但后者必须存在某个地方。 3 从安全性上,为了防止泄漏,access token 一般不会有很长的有效期。这里,“泄漏”的方式,除了楼主说的 storage 外,还包括比如日志,非 https 的 cookie ,不可信的第三方等等。显然,经常使用的 access token, 要比不经常使用的 refresh token 泄漏的概率高。此外,refresh token 是要配合 client id/secret 一起使用的,通常 client id/secret 不会和 refresh token 存在一个地方。 |
30
whileFalse 2021-12-09 16:26:02 +08:00 via iPhone
为了把你踢下线。(服务器主动使登陆状态失效)
session 机制只要删除服务器 redis key 即可把你踢掉 token 机制中 access token 是离线验证的不走 redis ,所以用一个较短的寿命,等他过期了你拿着 refresh token 去续期的时候服务器不给你续,就是踢下线了。 |
31
AsherTan 2021-12-09 16:45:54 +08:00
1. AccessToken 和 RefreshToken 一般都是配合着使用,多用于第三方 openAPI 的鉴权,自己后端服务的登录鉴权的 token 一般不会弄个 RefreshToken 的。
2. AccessToken 和 RefreshToken 基于安全性考虑一般都是建议保存在服务端的,是由服务端去使用,不会在客户端直接使用 Access Token 的。 3. AccessToken 还是基于安全考虑,有效时间比较短,一般两小时。可以使用 RefreshToken 定时请求接口获取新的 AccessToken 。 |
32
Akesudia 2021-12-09 17:09:18 +08:00
1.Refresh Token 确实提高了安全性,短有效期就是防止 Access Token 被窃取后攻击者长期保持有效会话,如果使用了 Token 黑名单机制就无所谓了(但破坏了 stateless)
2.Access Token 在每个请求中附带,Refresh Token 只在 Access Token 失效时传输,被中间人窃取机会比 Access Token 小。 |
33
unco020511 2021-12-09 17:25:50 +08:00
@VeryZero #20 我还真没见过每次都携带 refresh token 的...
|
34
SheHuannn 2021-12-09 17:38:17 +08:00
大致看了上边的回复,access token + refresh token 的模式更多的是解决了 access token 续签的问题,对改进用户体验有帮助,好像在保证数据安全性方面没起到啥实质性作用。
|
35
ipwx 2021-12-09 17:41:40 +08:00
加密签名,可以验证 AccessToken 没有被篡改和捏造(解密出来“什么时候过期”,它就一定是正确的过期时间。客户端无法捏造这个时间)。那么每个后端服务器只要有一套解密的密钥就行了。
甚至集中的认证服务器,可以根据要访问的业务,调用不同的加密密钥去加密,这样的 Access Token 自然只有对应的业务才会承认。 这就是 Stateless 的核心 —— AccessToken 不用连接任何中央服务器,就能迅速检验它是合法的、不是捏造的。时间戳附带签名就能证明其真实性。 |
36
chi1st 2021-12-09 17:43:44 +08:00
求问 RefreshToken 到底应该放在哪里储存呢?
|
37
mrliusg 2021-12-09 19:57:48 +08:00
RFC6819 里详细解释了 OAuth 2.0 面临的威胁和应该做的安全设计。从整体上看就可以理解为什么要设计两个 Token ,且具有不同的特性。
|
38
dany813 2021-12-10 11:45:41 +08:00
RefreshToken 一般放在 redis 吗
|
39
daimubai 2021-12-10 22:32:35 +08:00
@Qsong 请教下,“当 token 过期后,后端会利用 refeshToken 返回一个新的 token 给到前端使用”,在什么时候返回呢。是后端返给前端 403 ,然后前端去调获取新 token 的接口拿到新的 token ,然后再二次重试上一次的请求吗
|
41
liuxu 2021-12-16 22:13:28 +08:00 1
今天我也遇到这个问题,所以详细看了下 oauth2 refresh token 的 rfc ,https://datatracker.ietf.org/doc/html/rfc6749#section-1.5
简单的说,安全有等级,即使用 rsa 加密,是要不限制时间,你算个千百万年,也可以破解出来 access token 有 scope 限制资源范围,它只用于用户和资源方交互(资源方转而和授权方交互),同时限制有效时间,这样它存储在用户 client 端,如浏览器中,所以即使被盗取后也只能被盗取有限的信息,攻击面很窄 refresh token 一般只存储在绝对安全的环境,如用户 server 端,而且它只用于和授权方交互,更新 access token 等 oauth2 获取 access token 和 refresh token 都是强制要求 https 环境,也即是要保证信道安全,避免被网络劫持获取到 同时 oauth2 强制要求 refresh token 使用时必须能验证使用者身份来源,避免被冒充,方案一般有限制 refresh token 只能使用一次,这样攻击者获取到使用后,你再使用授权方会通知你 refresh token 已被使用,这样就可以发现当前用户 server 环境有安全问题,需要修复 或者限制 ip 白名单等 |
42
ioufev 2022-12-30 09:44:58 +08:00
正好和人最近讨论这个 token 刷新问题。
比如 token 有效期时 30 分钟, 用户登录 20 分钟后,有操作,需要续期。 否则用户正操作着呢,突然需要重新登录,体验太差了。 续期操作分前端和后端两种方式。 后端方式,简单粗暴就是将 token 的 redis 有效期重新设为 30 分钟,不过这样 token 自带的有效期就用不上了,不太推荐。 前端方式,token 剩余 10 分钟时,使用旧的 token ,申请个新的 token 。 |