我希望对新的REST API实现基于jwt的身份验证。但是由于到期时间是在令牌中设置的,那么是否可以自动延长它呢?我不希望用户每隔X分钟就需要登录一次,如果他们在这段时间内积极使用应用程序的话。这将是一个巨大的用户体验失败。

但是延长过期时间会创建一个新的令牌(旧的令牌在过期前仍然有效)。在每个请求后生成一个新的令牌对我来说听起来很傻。当多个令牌同时有效时,听起来像是一个安全问题。当然,我可以使用黑名单使旧的使用无效,但我需要存储令牌。JWT的好处之一是没有存储空间。

我发现Auth0是如何解决这个问题的。他们不仅使用JWT令牌,还使用refresh令牌: https://auth0.com/docs/tokens/refresh-tokens

但是,要实现这一点(没有Auth0),我需要存储刷新令牌并维护它们的过期。那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT)并将过期时间保存在服务器上呢?

还有其他选择吗?使用JWT不适合这种情况吗?


当前回答

当我在后端将我们的应用程序移动到带有RESTful api的HTML5时,我正在进行修补。我想到的解决办法是:

成功登录后,客户端将获得一个会话时间为30分钟(或通常的服务器端会话时间)的令牌。 创建一个客户端计时器来调用服务,以便在令牌到期之前更新令牌。新的令牌将在未来的调用中取代现有的令牌。

如您所见,这减少了频繁的刷新令牌请求。如果用户在更新令牌调用触发之前关闭浏览器/应用程序,之前的令牌将及时过期,用户将不得不重新登录。

可以采用更复杂的策略来应对用户不活跃(例如忽略打开的浏览器选项卡)。在这种情况下,更新令牌调用应该包括预期的到期时间,该时间不应该超过所定义的会话时间。应用程序必须相应地跟踪最后一次用户交互。

我不喜欢设置较长的过期时间,因此这种方法可能不适用于需要较少频繁身份验证的本机应用程序。

其他回答

今天,许多人选择使用jwt进行会话管理,却没有意识到他们为了简单而放弃了什么。我的回答详细阐述了问题的第二部分:

那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT)并将过期时间保存在服务器上呢? 还有其他选择吗?使用JWT不适合这种情况吗?

jwt能够支持基本的会话管理,但有一些限制。由于是自描述令牌,它们在服务器端不需要任何状态。这使得他们很有吸引力。例如,如果服务没有持久层,它就不需要仅仅为了会话管理而引入持久层。

然而,无国籍也是他们缺点的主要原因。由于它们只发布一次,内容固定且到期,因此您无法使用典型的会话管理设置完成您想做的事情。

也就是说,您不能按需使它们失效。这意味着您无法实现安全注销,因为没有办法使已经发出的令牌过期。出于同样的原因,您也不能实现空闲超时。一个解决方案是保留一个黑名单,但这会引入状态。

我写了一篇文章详细解释了这些缺点。需要明确的是,您可以通过添加更复杂的内容(滑动会话、刷新令牌等)来解决这些问题。

至于其他选项,如果您的客户端仅通过浏览器与您的服务交互,我强烈建议使用基于cookie的会话管理解决方案。我还列出了目前在web上广泛使用的认证方法。

我知道这是一个老问题,但我同时使用会话和令牌身份验证。我的应用程序是微服务的组合,所以我需要使用基于令牌的身份验证,这样每个微服务都不需要访问集中的数据库进行身份验证。我向我的用户发出2个jwt(由不同的秘密签名):

A standard JWT, used to authenticate requests. This token expires after 15 minutes. A JWT that acts as a refresh token that is placed in a secure cookie. Only one endpoint (actually it is its own microservice) accepts this token, and it is the JWT refresh endpoint. It must be accompanied by a CSRF token in the post body to prevent CRSF on that endpoint. The JWT refresh endpoint stores a session in the database (the id of the session and the user are encoded into the refresh JWT). This allows the user, or an admin, to invalidate a refresh token as the token must both validate and match the session for that user.

这工作得很好,但比使用基于会话的认证和cookie和CSRF令牌要复杂得多。因此,如果你没有微服务,那么基于会话的认证可能是可行的方法。

以下是撤销JWT访问令牌的步骤:

1) When you do login, send 2 tokens (Access token, Refresh token) in response to client . 2) Access token will have less expiry time and Refresh will have long expiry time . 3) Client (Front end) will store refresh token in his local storage and access token in cookies. 4) Client will use access token for calling apis. But when it expires, pick the refresh token from local storage and call auth server api to get the new token. 5) Your auth server will have an api exposed which will accept refresh token and checks for its validity and return a new access token. 6) Once refresh token is expired, User will be logged out.

如果你需要更多细节,请告诉我,我也可以分享代码(Java + Spring引导)。

如果您正在使用AWS Amplify & Cognito,这将为您带来魔力:

Use Auth.currentSession() to get the current valid token or get new if the current has expired. Amplify will handle it As a fallback, use some interval job to refresh tokens on demand every x minutes, maybe 10 min. This is required when you have a long-running process like uploading a very large video which will take more than an hour (maybe due to a slow network) then your token will expire during the upload and amplify will not update automatically for you. In this case, this strategy will work. Keep updating your tokens at some interval. How to refresh on demand is not mentioned in docs so here it is.

import { Auth } from 'aws-amplify';

try {
  const cognitoUser = await Auth.currentAuthenticatedUser();
  const currentSession = await Auth.currentSession();
  cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
    console.log('session', err, session);
    const { idToken, refreshToken, accessToken } = session;
    // do whatever you want to do now :)
  });
} catch (e) {
  console.log('Unable to refresh Token', e);
}

来源:https://github.com/aws-amplify/amplify-js/issues/2560

这个方法怎么样:

对于每个客户端请求,服务器将令牌的过期时间与(currentTime - lastAccessTime)进行比较。 如果expirationTime < (currentTime - lastAccessedTime),则将上一次lastAccessedTime更改为currentTime。 如果浏览器上的不活动时间超过了过期时间,或者如果浏览器窗口被关闭并且过期时间为> (currentTime - lastAccessedTime),那么服务器可以使令牌过期并要求用户再次登录。

在这种情况下,刷新令牌不需要额外的端点。 将感激任何反馈。