我可以强制注销或过期 JWT 令牌吗? [英] Can I force a logout or expiration of a JWT token?

查看:20
本文介绍了我可以强制注销或过期 JWT 令牌吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我们正在使用 JWT 进行身份验证,因此一旦创建了令牌,它就会终身创建,如果我们设置时间过期,令牌就会过期.

For authentication currently we are using JWT, so once a token is created it's created for a lifetime, and if we set a time expire, the token will expire.

有没有办法让令牌过期?

Is there any way to expire token?

单击注销按钮时,我需要销毁令牌.

While clicking log out button, I need to destroy the token.

我正在使用 ASP.NET Core WebAPI.

I'm using ASP.NET Core WebAPI.

推荐答案

我认为取消 JWT 是处理注销的最佳方式.Piotr 在他的博客中解释得很好:取消 JWT 令牌

I think cancelling JWT is the best way to handle logout. Piotr explained well in his blog: Cancel JWT tokens

我们将从界面开始:

public interface ITokenManager
{
    Task<bool> IsCurrentActiveToken();
    Task DeactivateCurrentAsync();
    Task<bool> IsActiveAsync(string token);
    Task DeactivateAsync(string token);
}

及其实现的过程,基本思想是保持仅跟踪停用的令牌,并在没有时将其从缓存中删除不再需要(意思是当到期时间过去时)——他们不会仍然有效.

And process with its implementation, where the basic idea is to keep track of deactivated tokens only and remove them from a cache when not needed anymore (meaning when the expiry time passed) – they will be no longer valid anyway.

public class TokenManager : ITokenManager
{
    private readonly IDistributedCache _cache;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IOptions<JwtOptions> _jwtOptions;
 
    public TokenManager(IDistributedCache cache,
            IHttpContextAccessor httpContextAccessor,
            IOptions<JwtOptions> jwtOptions
        )
    {
        _cache = cache;
        _httpContextAccessor = httpContextAccessor;
        _jwtOptions = jwtOptions;
    }
 
    public async Task<bool> IsCurrentActiveToken()
        => await IsActiveAsync(GetCurrentAsync());
 
    public async Task DeactivateCurrentAsync()
        => await DeactivateAsync(GetCurrentAsync());
 
    public async Task<bool> IsActiveAsync(string token)
        => await _cache.GetStringAsync(GetKey(token)) == null;
 
    public async Task DeactivateAsync(string token)
        => await _cache.SetStringAsync(GetKey(token),
            " ", new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow =
                    TimeSpan.FromMinutes(_jwtOptions.Value.ExpiryMinutes)
            });
 
    private string GetCurrentAsync()
    {
        var authorizationHeader = _httpContextAccessor
            .HttpContext.Request.Headers["authorization"];
 
        return authorizationHeader == StringValues.Empty
            ? string.Empty
            : authorizationHeader.Single().Split(" ").Last();
    }
    
    private static string GetKey(string token)
        => $"tokens:{token}:deactivated";
}

如您所见,有 2 个辅助方法将使用当前HttpContext 让事情变得更简单.

As you can see, there are 2 helper methods that will use the current HttpContext in order to make things even easier.

接下来,让我们创建一个中间件来检查令牌是否停用与否.这就是为什么我们应该将它们保存在缓存中的原因- 用每个请求访问数据库可能会杀死迟早你的应用程序(或者至少让它变得非常非常慢):

Next, let’s create a middleware that will check if the token was deactivated or not. That’s the reason why we should keep them in cache – hitting the database with every request instead would probably kill your app sooner or later (or at least make it really, really slow):

public class TokenManagerMiddleware : IMiddleware
{
    private readonly ITokenManager _tokenManager;
 
    public TokenManagerMiddleware(ITokenManager tokenManager)
    {
        _tokenManager = tokenManager;
    }
    
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        if (await _tokenManager.IsCurrentActiveToken())
        {
            await next(context);
            
            return;
        }
        context.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
    }
}

最后,让我们通过实现一个端点来完成我们的旅程取消令牌:

Eventually, let’s finish our journey with implementing an endpoint for canceling the tokens:

[HttpPost("tokens/cancel")]
public async Task<IActionResult> CancelAccessToken()
{
    await _tokenManager.DeactivateCurrentAsync();
 
    return NoContent();
}

当然,我们可以通过传递令牌使其更复杂通过 URL,或一次取消所有现有的用户令牌(这将需要额外的实现来跟踪他们),但这是一个正常工作的基本示例.

For sure, we could make it more sophisticated, via passing the token via URL, or by canceling all of the existing user tokens at once (which would require an additional implementation to keep track of them), yet this is a basic sample that just works.

确保您将在您的容器并配置中间件:

Make sure that you will register the required dependencies in your container and configure the middleware:

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddTransient<TokenManagerMiddleware>();
    services.AddTransient<ITokenManager, Services.TokenManager>();
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddDistributedRedisCache(r => { r.Configuration = Configuration["redis:connectionString"]; 
    ...
}
 
public void Configure(IApplicationBuilder app, IHostingEnvironment env,
    ILoggerFactory loggerFactory)
{
    ...
    app.UseAuthentication();
    app.UseMiddleware<TokenManagerMiddleware>();
    app.UseMvc();
}

并在 appsettings.json 文件中提供 Redis 的配置:

And provide a configuration for Redis in appsettings.json file:

"redis": {
  "connectionString": "localhost"
}

现在尝试运行应用程序并调用令牌取消[原文如此]端点——就是这样.

Try to run the application now and invoke the token cancellation[sic] endpoint – that’s it.

这篇关于我可以强制注销或过期 JWT 令牌吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆