JWT(JSON Web令牌)自动延长到期
我想为我们的新REST API实现基于JWT的authentication。 但是因为到期是在令牌中设定的,有可能自动延长吗? 我不希望用户在每个X分钟后都需要login,如果他们在那段时间内正在使用该应用程序。 这将是一个巨大的用户体验失败。
但延长期限会创build一个新的标记(旧的标记在到期之前仍然有效)。 每个请求后生成一个新的令牌听起来很愚蠢。 听起来像是一个安全问题,当多个令牌同时有效时。 当然,我可以使用黑名单使旧的使用无效,但我需要存储令牌。 JWT的好处之一就是没有存储空间。
我发现Auth0如何解决它。 他们不仅使用JWT令牌,而且使用刷新令牌: https : //docs.auth0.com/refresh-token
但是,为了实现这一点(没有Auth0),我需要存储刷新令牌并保持其到期。 那么真正的好处是什么? 为什么不只有一个令牌(不是JWT),并保持到服务器上到期?
还有其他的select吗? 是否使用JWT不适合这种情况?
我在Auth0工作,并参与了刷新令牌function的devise。
这一切都取决于应用程序的types,这是我们推荐的方法。
Web应用程序
一个好的模式是在令牌到期之前刷新令牌。
将令牌过期设置为一周,每次用户打开Web应用程序并每隔一小时刷新一次令牌。 如果用户没有打开应用程序超过一个星期,他们将不得不再次login,这是可以接受的Web应用程序用户体验。
要刷新令牌,您的API需要一个接收有效的未过期的JWT的新端点,并将带有新签名的JWT与新的过期字段一起返回。 然后,Web应用程序将在某处存储令牌。
移动/本机应用程序
大多数本机应用程序只能login一次。
这个想法是,刷新令牌永远不会过期,它可以永远交换有效的智威汤逊。
永不过期的令牌的问题永远不意味着永远不会。 如果你丢失手机,你会怎么做? 所以,它需要被用户以某种方式识别,并且应用程序需要提供一种撤销访问的方式。 我们决定使用设备的名称,例如“maryo的iPad”。 然后用户可以去应用程序,并撤销访问“maryo的iPad”。
另一种方法是在特定事件上撤销刷新令牌。 一个有趣的事情是改变密码。
我们认为JWT对这些用例没有用处,所以我们使用一个随机生成的string,并将其存储在我们这边。
在你自己处理auth的情况下(即不要使用像Auth0这样的提供者),下面的内容可能会起作用:
- 发行相对较短的JWT令牌,比如说15分钟。
- 应用程序在任何需要令牌的交易(令牌包含到期date)之前检查令牌过期date。 如果令牌已经过期,那么它首先要求API“刷新”令牌(这对UX来说是透明的)。
- API获取令牌刷新请求,但首先检查用户数据库以查看是否针对该用户configuration文件设置了“reauth”标志(令牌可以包含用户标识)。 如果该标志存在,则令牌刷新被拒绝,否则发出新的令牌。
- 重复。
例如,当用户重置密码时,数据库后端的'reauth'标志将被设置。 用户下次login时,该标志被删除。
另外,假设您有一项政策,即用户必须每72小时至lesslogin一次。 在这种情况下,您的API令牌刷新逻辑还会检查用户数据库中用户的上次logindate,并在此基础上拒绝/允许令牌刷新。
当我们将应用程序移动到后端的RESTful API时,我正在修补程序。 我提出的解决scheme是:
- 客户端被授予会话时间为30分钟(或任何通常的服务器端会话时间)成功login的令牌。
- 创build一个客户端计时器来调用服务,以在其到期时间之前更新令牌。 新的令牌将取代现有的未来呼叫。
正如你所看到的,这减less了频繁的刷新令牌请求。 如果用户在更新令牌调用被触发之前closures了浏览器/应用程序,则以前的令牌将及时过期,用户将不得不重新login。
可以实施更复杂的策略以迎合用户不活动(例如,忽略打开的浏览器标签)。 在这种情况下,更新令牌调用应该包括预期的到期时间,不应超过定义的会话时间。 应用程序将不得不跟踪最后的用户交互。
我不喜欢设置长时间到期的想法,因此这种方法可能不适用于需要较低频率validation的本地应用程序。
智威汤逊-自动刷新
如果你正在使用节点(React / Redux / Universal JS),你可以安装npm i -S jwt-autorefresh
。
该库在访问令牌到期之前计算用户计算的秒数(基于令牌中编码的exp声明)时,刷新JWT令牌。 它有一个广泛的testing套件,并检查相当多的条件,以确保任何奇怪的活动都伴随着一个关于你的环境configuration错误的描述性信息。
完整的示例实现
import autorefresh from 'jwt-autorefresh' /** Events in your app that are triggered when your user becomes authorized or deauthorized. */ import { onAuthorize, onDeauthorize } from './events' /** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */ const refresh = () => { const init = { method: 'POST' , headers: { 'Content-Type': `application/x-www-form-urlencoded` } , body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token` } return fetch('/oauth/token', init) .then(res => res.json()) .then(({ token_type, access_token, expires_in, refresh_token }) => { localStorage.access_token = access_token localStorage.refresh_token = refresh_token return access_token }) } /** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */ const leadSeconds = () => { /** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */ const jitter = Math.floor(Math.random() * 30) /** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */ return 60 + jitter } let start = autorefresh({ refresh, leadSeconds }) let cancel = () => {} onAuthorize(access_token => { cancel() cancel = start(access_token) }) onDeauthorize(() => cancel())
免责声明:我是维护者
我实际上在PHP中使用Guzzle客户端来实现api的客户端库,但这个概念应该适用于其他平台。
基本上,我发出两个令牌,一个短的(5分钟)和一个长期的一个星期后过期。 客户端库使用中间件尝试刷新短令牌,如果它收到一个请求的401响应。 然后,它会再次尝试原始请求,并且如果能够刷新,就会以透明的方式获得正确的响应。 如果失败了,它只会把401发送给用户。
如果短标记已过期,但是仍然可信,并且长标记有效且可信,则将使用长标记authentication的服务上的特定端点(这是其唯一可用的)来刷新短标记。 然后,它将使用短令牌获得新的长令牌,从而每当刷新短令牌时再延长一周。
这种方法还允许我们在至多5分钟内撤销访问,这是我们可以接受的,而不必存储令牌黑名单。
后期编辑:重新阅读这个月后,我的头脑里新鲜的,我应该指出,刷新短期令牌时,可以撤销访问,因为它提供了一个更昂贵的电话的机会(例如调用数据库,看看用户已被禁止),而无需每次打电话向您的服务付费。
好的问题 – 在问题本身中有丰富的信息。
文章刷新令牌:什么时候使用它们以及它们如何与JWT交互提供了一个好方法。 有几点是:
- 刷新令牌携带获取新访问令牌所需的信息。
- 刷新令牌也可以过期,但寿命相当长。
- 刷新令牌通常受到严格的存储要求,以确保它们不泄漏。
- 它们也可以被授权服务器列入黑名单。
另外看看auth0 / angular-jwt的 angularjs
对于Web API。 请阅读使用ASP .NET Web API 2和Owin在AngularJS App中启用OAuth刷新令牌
在JWT失效的情况下,在后端没有任何额外的安全存储的情况下,另一种解决scheme是在users表上实现一个新的jwt_version
整数列。 如果用户希望注销或过期现有的令牌,他们只需增加jwt_version
字段。
在生成新的JWT时,将jwt_version
编码到JWT负载中,如果新的JWT应该replace所有其他JWT,则可以预先递增值。
在validationJWT时, jwt_version
字段与user_id
jwt_version
进行比较,只有匹配才授予授权。
这种方法如何:
- 对于每个客户端请求,服务器将令牌的expirationTime与(currentTime – lastAccessTime)
- 如果expirationTime <(currentTime – lastAccessedTime) ,则将lastAccessedTime更改为currentTime。
- 如果浏览器处于非活动状态的时间超过了expirationTime,或者浏览器窗口已closures, 并且expirationTime>(currentTime – lastAccessedTime) ,则服务器可以过期令牌并要求用户重新login。
在这种情况下,我们不需要额外的端点来刷新令牌。 将不胜感激任何feedack。
我通过在令牌数据中添加一个variables来解决这个问题:
softexp - I set this to 5 mins (300 seconds)
我将expiresIn
选项设置为我所需的时间,然后用户将被强制重新login。 我的设定为30分钟。 这必须大于softexp
的值。
当我的客户端应用程序向服务器API发送请求(需要标记,例如客户列表页面)时,服务器会根据其原始的expiresIn
( expiresIn
)值检查提交的标记是否仍然有效。 如果无效,服务器将以特定的状态响应此错误,例如。 INVALID_TOKEN
。
如果令牌仍然基于expiredIn
值有效,但是它已经超过了softexp
值,那么服务器将响应此错误的单独状态,例如。 EXPIRED_TOKEN
:
(Math.floor(Date.now() / 1000) > decoded.softexp)
在客户端,如果收到EXPIRED_TOKEN
响应,则应通过向服务器发送更新请求来自动更新令牌。 这对用户来说是透明的,并被自动照顾客户端应用程序。
服务器中的更新方法必须检查令牌是否仍然有效:
jwt.verify(token, secret, (err, decoded) => {})
如果上述方法失败,服务器将拒绝更新令牌。