【问题标题】:Can I force a logout or expiration of a JWT token?我可以强制注销或过期 JWT 令牌吗?
【发布时间】:2020-08-05 16:33:52
【问题描述】:

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

有没有办法让令牌过期?

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

我正在使用 ASP.NET Core WebAPI。

【问题讨论】:

标签: jwt asp.net-core-webapi


【解决方案1】:

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

我们将从界面开始:

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

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

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 让事情变得更简单。

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

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;
    }
}

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

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

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

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

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 的配置:

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

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

【讨论】:

    【解决方案2】:

    实际上最好的退出方式就是从客户端移除令牌。您可以缩短令牌的生命周期(5-15 分钟)并实施刷新令牌以增加安全性。在这种情况下,攻击者使用您的 JWT 做某事的机会就更少了

    【讨论】:

    • 是的@Mateech,也试过了,但我们在灰盒测试中失败了。
    猜你喜欢
    • 2020-02-20
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 2020-11-02
    • 2019-01-07
    • 2018-01-26
    相关资源
    最近更新 更多