我希望对新的REST API实现基于jwt的身份验证。但是由于到期时间是在令牌中设置的,那么是否可以自动延长它呢?我不希望用户每隔X分钟就需要登录一次,如果他们在这段时间内积极使用应用程序的话。这将是一个巨大的用户体验失败。
但是延长过期时间会创建一个新的令牌(旧的令牌在过期前仍然有效)。在每个请求后生成一个新的令牌对我来说听起来很傻。当多个令牌同时有效时,听起来像是一个安全问题。当然,我可以使用黑名单使旧的使用无效,但我需要存储令牌。JWT的好处之一是没有存储空间。
我发现Auth0是如何解决这个问题的。他们不仅使用JWT令牌,还使用refresh令牌:
https://auth0.com/docs/tokens/refresh-tokens
但是,要实现这一点(没有Auth0),我需要存储刷新令牌并维护它们的过期。那么真正的好处是什么呢?为什么不只有一个令牌(不是JWT)并将过期时间保存在服务器上呢?
还有其他选择吗?使用JWT不适合这种情况吗?
如果您正在使用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
当我在后端将我们的应用程序移动到带有RESTful api的HTML5时,我正在进行修补。我想到的解决办法是:
成功登录后,客户端将获得一个会话时间为30分钟(或通常的服务器端会话时间)的令牌。
创建一个客户端计时器来调用服务,以便在令牌到期之前更新令牌。新的令牌将在未来的调用中取代现有的令牌。
如您所见,这减少了频繁的刷新令牌请求。如果用户在更新令牌调用触发之前关闭浏览器/应用程序,之前的令牌将及时过期,用户将不得不重新登录。
可以采用更复杂的策略来应对用户不活跃(例如忽略打开的浏览器选项卡)。在这种情况下,更新令牌调用应该包括预期的到期时间,该时间不应该超过所定义的会话时间。应用程序必须相应地跟踪最后一次用户交互。
我不喜欢设置较长的过期时间,因此这种方法可能不适用于需要较少频繁身份验证的本机应用程序。
services.Configure (Configuration.GetSection(“ApplicationSettings”));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<AuthenticationContext>();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
}
);
services.AddCors();
//Jwt Authentication
var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x=> {
x.RequireHttpsMetadata = false;
x.SaveToken = false;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
}
我知道这是一个老问题,但我同时使用会话和令牌身份验证。我的应用程序是微服务的组合,所以我需要使用基于令牌的身份验证,这样每个微服务都不需要访问集中的数据库进行身份验证。我向我的用户发出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令牌要复杂得多。因此,如果你没有微服务,那么基于会话的认证可能是可行的方法。
实际上,我使用Guzzle客户端在PHP中实现了这一点,为api制作了一个客户端库,但这个概念应该适用于其他平台。
基本上,我发行了两个代币,一个短的(5分钟),一个长的,一周后到期。如果客户机库接收到对某个请求的401响应,则使用中间件尝试对短令牌进行一次刷新。然后,它将再次尝试原始请求,如果它能够刷新,就会获得正确的响应,对用户透明。如果失败了,它会把401发给用户。
如果短令牌过期了,但仍然是可信的,而长令牌是有效且可信的,它将使用长令牌验证的服务上的一个特殊端点刷新短令牌(这是它唯一可以用于的事情)。然后,它将使用短令牌来获得一个新的长令牌,从而在每次刷新短令牌时将其延长一周。
这种方法还允许我们在最多5分钟内撤销访问,这对于我们的使用是可以接受的,而不必存储令牌黑名单。
后期编辑:在我脑海中记忆犹新的几个月后重新阅读这篇文章,我应该指出,你可以在刷新短令牌时撤销访问权,因为它为更昂贵的调用提供了机会(例如调用数据库来查看用户是否已被禁止),而无需为每一次对你的服务的调用付费。
以下是撤销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引导)。