在后端没有任何额外安全存储的情况下,使JWT无效的替代解决方案是实现新的 jwt_version users表上的整数列。如果用户希望注销或使现有令牌过期,他们只需增加 jwt_version 领域。
jwt_version
生成新的JWT时,编码 jwt_version 进入JWT有效负载,如果新的JWT应该替换所有其他有效负载,可以选择预先增加该值。
在验证JWT时, jwt_version 田野与之相比较 user_id 授权仅在匹配时授予。
user_id
我通过在令牌数据中添加一个变量来解决这个问题:
softexp - I set this to 5 mins (300 seconds)
我设置 expiresIn 在用户被迫再次登录之前,我所需时间的选项。我的设定为30分钟。这必须大于。的值 softexp 。
expiresIn
softexp
当我的客户端应用程序向服务器API(需要令牌,例如客户列表页面)发送请求时,服务器会根据其原始到期时间检查提交的令牌是否仍然有效( expiresIn )价值。如果它无效,服务器将以特定于此错误的状态进行响应,例如。 INVALID_TOKEN 。
INVALID_TOKEN
如果令牌仍然有效,则基于 expiredIn 价值,但它已经超过了 softexp 值,服务器将以此错误的单独状态响应,例如。 EXPIRED_TOKEN :
expiredIn
EXPIRED_TOKEN
(Math.floor(Date.now() / 1000) > decoded.softexp)
在客户端,如果收到 EXPIRED_TOKEN 响应,它应该通过向服务器发送续订请求自动更新令牌。这对用户是透明的,并自动处理客户端应用程序。
服务器中的续订方法必须检查令牌是否仍然有效:
jwt.verify(token, secret, (err, decoded) => {})
如果上述方法失败,服务器将拒绝续订令牌。
在后端使用RESTful apis将应用程序移动到HTML5时,我正在修补。我想出的解决方案是:
如您所见,这减少了频繁的刷新令牌请求。如果用户在触发续订令牌呼叫之前关闭浏览器/应用程序,则先前令牌将及时到期,用户必须重新登录。
可以实现更复杂的策略以满足用户不活动(例如忽略打开的浏览器选项卡)。在这种情况下,续订令牌调用应包括预期的到期时间,该时间不应超过定义的会话时间。应用程序必须相应地跟踪最后的用户交互。
我不喜欢设置长期到期的想法,因此这种方法可能不适用于需要较少频繁身份验证的本机应用程序。
如果您使用的是节点(React / Redux / Universal JS),则可以安装 npm i -S jwt-autorefresh 。
npm i -S jwt-autorefresh
此库根据用户计算的访问令牌到期之前的秒数(基于令牌中编码的exp声明)计划刷新JWT令牌。它有一个广泛的测试套件,可以检查很多条件,以确保任何奇怪的活动都伴随着有关环境配置错误的描述性消息。
的 完整的示例实现 强>
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())
的 免责声明:我是维护者 强>
在您自己处理auth的情况下(即不使用Auth0之类的提供程序),以下内容可能有效:
例如,当用户重置密码时,将设置数据库后端中的“reauth”标志。用户下次登录时会删除该标志。
此外,假设您有一项政策,用户必须至少每72小时登录一次。在这种情况下,您的API令牌刷新逻辑还将检查用户从用户数据库的上次登录日期,并在此基础上拒绝/允许令牌刷新。
我实际上是在PHP中使用Guzzle客户端为api创建了一个客户端库,但这个概念应该适用于其他平台。
基本上,我发行了两个令牌,一个短的(5分钟)和一个长的,在一周后到期。如果客户端库收到对某个请求的401响应,则使用中间件尝试刷新短令牌。然后它将再次尝试原始请求,如果能够刷新,则对用户透明地获得正确的响应。如果失败,它只会将401发送给用户。
如果短令牌已过期,但仍然是可信的并且长令牌有效且可信,则它将使用长令牌进行身份验证的服务上的特殊端点刷新短令牌(这是唯一可以使用的)。然后它将使用短令牌获取新的长令牌,从而每次刷新短令牌时将其延长一周。
这种方法还允许我们在最多5分钟内撤销访问权限,这对于我们的使用是可以接受的,而不必存储令牌的黑名单。
延迟编辑:重新阅读这几个月后,我应该指出你可以在刷新短令牌时撤销访问权限,因为它提供了更昂贵的调用的机会(例如,调用数据库以查看用户是否已被禁止),无需在每次拨打您的服务时付费。
好问题 - 问题本身就有丰富的信息。
这篇文章 刷新令牌:何时使用它们以及它们如何与JWT交互 为这种情况提供了一个好主意。一些要点是: -
另外看一看 auth0 /角JWT angularjs
对于Web API。读 使用ASP .NET Web API 2和Owin在AngularJS App中启用OAuth刷新令牌
以下是撤销JWT访问令牌的步骤:
1)登录时,发送2个令牌(访问令牌,刷新令牌)以响应客户端。 2)访问令牌将具有较少的到期时间,刷新将具有较长的到期时间。 3)客户端(前端)将刷新令牌存储在其本地存储中并访问令牌中的令牌。 4)客户端将使用访问令牌来调用apis。但是当它到期时,从本地存储中选择刷新令牌并调用auth server api以获取新令牌。 5)您的auth服务器将暴露api,它将接受刷新令牌并检查其有效性并返回新的访问令牌。 6)刷新令牌过期后,用户将被注销。
如果您需要更多详细信息,请告诉我,我也可以共享代码(Java + Spring启动)。
这种方法怎么样:
在这种情况下,我们不需要额外的终点来刷新令牌。 非常感谢任何feedack。
我在Auth0工作,我参与了刷新令牌功能的设计。
这一切都取决于应用程序的类型,这是我们推荐的方法。
一个好的模式是在令牌过期之前刷新令牌。
将令牌过期设置为一周,并在每次用户打开Web应用程序时每隔一小时刷新令牌。如果用户未打开应用程序超过一周,则必须再次登录,这是可接受的Web应用程序UX。
要刷新令牌,您的API需要一个新端点,该端点接收有效的,未过期的JWT,并返回带有新过期字段的相同签名JWT。然后,Web应用程序将令牌存储在某处。
大多数本机应用程序只登录一次。
我们的想法是刷新令牌永不过期,并且可以始终为有效的JWT进行交换。
令牌永不过期的问题是 的 决不 强> 从不意味着。如果丢失手机怎么办?因此,它需要以某种方式由用户识别,并且应用程序需要提供撤销访问的方法。我们决定使用设备的名称,例如“maryo的iPad”。然后,用户可以转到该应用程序并撤消对“maryo的iPad”的访问权限。
另一种方法是撤消特定事件上的刷新令牌。一个有趣的事件是更改密码。
我们认为JWT对这些用例没用,所以我们使用随机生成的字符串,然后将它存储在我们这边。