【发布时间】:2020-08-05 16:33:52
【问题描述】:
目前我们使用 JWT 进行身份验证,因此一旦创建了令牌,它就会终身创建,如果我们设置时间过期,令牌就会过期。
有没有办法让令牌过期?
单击注销按钮时,我需要销毁令牌。
我正在使用 ASP.NET Core WebAPI。
【问题讨论】:
目前我们使用 JWT 进行身份验证,因此一旦创建了令牌,它就会终身创建,如果我们设置时间过期,令牌就会过期。
有没有办法让令牌过期?
单击注销按钮时,我需要销毁令牌。
我正在使用 ASP.NET Core WebAPI。
【问题讨论】:
我认为取消 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" }现在尝试运行应用程序并调用令牌取消[原文如此] 端点——就是这样。
【讨论】:
实际上最好的退出方式就是从客户端移除令牌。您可以缩短令牌的生命周期(5-15 分钟)并实施刷新令牌以增加安全性。在这种情况下,攻击者使用您的 JWT 做某事的机会就更少了
【讨论】: