已经有Access Token,为什么还需要Refresh Token?
发布于: 2025-3-7
最后更新: 2025-3-6
type
status
date
slug
summary
tags
category
icon
经常会在网络上看到有人在疑惑,在做鉴权的时候,已经有了一个Access Token,为什么还需要一个Refresh Token呢?我曾经也有过这样的困惑,甚至还实操过典型的错误案例,今天就来聊一聊关于Access Token和Refresh Token的那些事
为了方便介绍,后文统一把Access Token成为访问令牌,Refresh Token成为刷新令牌

凭证(Credential)

在网络上的绝大多数应用系统中,都会涉及到用户认证这一步骤,而凭证就是承载认证授权信息的载体。而对于“如何承载认证授权信息?”这一问题的不同开发代表了软件架构对待共享状态信息的两种不同思路:
  • 状态维护在服务端
  • 状态维护在客户端
在过去的三十几年里,以HTTP协议的Cookie-Session机制为代表的服务端状态存储一直以来都是主流的解决方案。直到最近的十年,由于分布式技术的发展和应用,再加上分布式系统中共享数据必然会受到CAP不兼容原理的打击限制,迫使人们重新去审视曾经被基本放弃掉的客户端存储,这就让原本通常只在多方系统中采用的JWT令牌方案,在分布式系统中也有了一块用武之地。

Cookie-Session

为了在使用HTTP发送请求时携带认证信息,RFC6265 规范定义了HTTP的状态管理机制,在HTTP协议中增加了Set-Cookie指令。该指令由服务端生成,以键值对的形式向客户端发送一组信息,在此后一段时间内的每一次HTTP请求中,客户端以名为Cookie的Header附带在请求中重新发回给服务端,以便服务端区分来自不同客户端的请求。一个典型的Set-Cookie指令如下所示:
客户端收到该指令以后,就会在后续一段时间的请求的Header中携带该信息,例如:
服务端根据每一个请求中的Cookie,就可以分辨出请求来自哪一个用户。
而像这样的信息,一般情况下系统会把状态信息保存在服务端,在Cookie里只传输一个无字面意义的、不重复的字符串,大多数时候都命名为sessionid这一类的名字,服务器拿这个sessionid作为key,在内存中以Key/Entity的结构存储每一个在线用户的上下文状态,再加上一些超时自动清理的管理措施。这种服务端的状态管理机制就是我们所说的Cookie-Session机制。
在单体架构中,Cookie-Session机制是最适合用于做认证的方案,但是当需要水平扩展能力,要部署集群的时候就麻烦了,由于session存储在服务端的内存中,水平扩展时需要增加服务器,那么就需要去考虑多台服务器中的session数据一致性的问题,为了在一定程度上减少问题的复杂性,于是JWT就来了。(注意,JWT只是一定程度上的替代品,并不是说JWT比Cookie-Session更先进,更不可能全面替代Cookie-Session)

JWT

JWT定义在RFC7519标准之中,是目前使用最广泛的一种令牌格式,尤其是搭配OAuth2,应用于分布式的、涉及到多方的应用系统之中。
一个典型的JWT的数据如下所示,这是JWT官网 jwt.io 官方的示例:
notion image
左边的那一串字符串是令牌,右边的Json结构是JWT令牌中携带的信息。最常见的使用方式是将令牌附在名为Authorization的Header中发送给服务端,而前缀在RFC 6750中被规定为Bearer,因此一次典型的使用了OAuth2认证的HTTP请求如下:
JWT在很多时候都会配合OAuth2使用,下面给出一个OAuth2的授权过程示例。

OAuth2的授权过程

OAuth2支持多种授权模式,在这里我只介绍一种简单并且常用的密码模式:
notion image
在上图中,第三方应用拿到访问令牌和刷新令牌以后,就可以去访问用户的资源,并且在令牌有效期内,也不会再和授权服务器进行交互。

JWT的问题是?

令牌难以主动失效

JWT令牌一签发,理论上就和认证服务器没有什么关系了,在令牌到期之前始终都会有效。于是在处理一些比较常见的需求就会遇到麻烦:
  • 用户A执行了违规操作,管理员需要将用户A踢下线
  • 某个用户先在设备1登录自己账号,然后这个用户在设备2登录自己账号时,需要让设备1的账号自动退出

解决办法

设计一个时限较长的刷新令牌和时限较短的访问令牌。访问令牌一般有效期就只有几个小时,过期以后就需要使用刷新令牌去获取新的访问令牌,这样就可以在一定程度减弱服务端无法主动让访问令牌失效的影响。
而时限较长的刷新令牌可以在服务端单独管理,由于刷新令牌的时限比较长,颁发刷新令牌的频率不会很高,数量也不会很多,当需要让某个用户或者某台设备登录状态失效时就去清楚掉它的刷新令牌,这样就实现了服务端主动下线的功能。
安全考虑,刷新令牌无法用于登录,只允许用来刷新访问令牌,即使刷新令牌被抓包拿走,当你刷新失败出发账号密码登录或者验证码登录,此时就会刷新令牌就会更新,对方也就无法再继续使用你的令牌。

总结

设计一个访问令牌和一个刷新令牌是为了减弱在使用OAuth2认证时,服务端无法主动让令牌失效的影响。同时正常用户的使用过程中,可以在很长时间内不触发登录流程,减少账户密码的传输频率能降低其被盗窃的风险,同时用户体验更好,而即使是非正常使用,由于服务端能够通过将刷新令牌失效来实现主动下线客户端,也能一定程度的保证安全。
 
人人都可以搭建自己的临时邮箱-ZMAILMac用户必备!轻松监控系统状态的开源神器-Stats
Loading...