【问题标题】:How to disable / invalidate JWT tokens?如何禁用/使 JWT 令牌无效?
【发布时间】:2020-02-09 01:56:21
【问题描述】:

这就是我为我的.NET Core API 创建 JWT 令牌的方式,它工作得非常好,但我想实现在 HTTP 请求来请求它时撤销、禁用或无效 JWT 令牌的可能性,使用令牌在标题中。

我可以想到一种方法,将令牌存储在我的数据库中,并有一个 boolean 列指示令牌是否处于活动状态,但是有没有办法在不将令牌存储在数据库中的情况下做到这一点全部?

public UserAuthenticationResponse CreateToken(UserAuthenticationRequest userAuth)
{
    var user = // try to find user in database...
    if (user == null)
    {
        return null;
    }

    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
    var tokenDescriptor = new SecurityTokenDescriptor
    {
        Subject = new ClaimsIdentity(new Claim[]
        {
            new Claim("user_id", userAuth.Id),
        }),
        Expires = DateTime.UtcNow.AddMinutes(5),
        SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
    };

    var token = tokenHandler.CreateToken(tokenDescriptor);
    var authenticatedUser = new UserAuthenticationResponse();
    authenticatedUser.Id = user.Id;
    authenticatedUser.Token = tokenHandler.WriteToken(token);

    return authenticatedUser;
}

【问题讨论】:

标签: .net-core jwt asp.net-core-webapi


【解决方案1】:

我最终所做的是创建了放在管道顶部的中间件。我持有一个List<string>,它代表一个列入黑名单的令牌列表(稍后我会在它们过期后清理它们)。

当我自己验证令牌时,我正在检查BlacklistToken(token, timeout) 方法。如果它返回 true,我可以将令牌列入黑名单,并且下次用户尝试使用该令牌访问某些内容时,它不会让他这样做。然后,稍后,我调用 CleanupTokens(tokenProvider) 获取所有令牌,检查它们是否已过期(感谢获取令牌过期日期的 tokenProvider),如果是,它将它们从列表中删除。

public class TokenPair
{
    [JsonProperty(PropertyName = "token")]
    public string Token { get; set; }
    [JsonProperty(PropertyName = "userId")]
    public string UserID { get; set; }
}

public interface ITokenLocker
{
    bool BlacklistToken(string token, int timeout);
    void CleanupTokens(ITokenProvider tokenProvider);
    bool IsBlacklisted(string token);
}

public class TokenLocker : ITokenLocker
{
    private List<string> _blacklistedTokens;

    public TokenLocker()
    {
        _blacklistedTokens = new List<string>();
    }

    public bool BlacklistToken(string token, int timeout)
    {
        lock (_blacklistedTokens)
        {
            if (!_blacklistedTokens.Any(x => x == token))
            {
                _blacklistedTokens.Add(token);
                return true;
            }
        }

        Thread.Sleep(timeout);

        lock (_blacklistedTokens)
        {
            if (!_blacklistedTokens.Any(x => x == token))
            {
                _blacklistedTokens.Add(token);
                return true;
            }
            else
                return false;
        }
    }

    public void CleanupTokens(ITokenProvider tokenProvider)
    {
        lock (_blacklistedTokens)
        {
            for (int i = 0; i < _blacklistedTokens.Count; i++)
            {
                var item = _blacklistedTokens[i];

                DateTime expiration = tokenProvider.GetExpiration(item);
                if (expiration < DateTime.UtcNow)
                {
                    _blacklistedTokens.Remove(item);
                    i--;
                }
            }
        }
    }

    public bool IsBlacklisted(string token)
    {
        return _blacklistedTokens.Any(tok => tok == token);
    }
}

public class TokenMiddleware
{
    private readonly RequestDelegate _next;

    public TokenMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    private string GetToken(HttpContext ctx)
    {
        ctx.Request.EnableBuffering();
        using (var reader = new StreamReader(ctx.Request.Body, Encoding.UTF8, true, 1024, true))
        {
            var jsonBody = reader.ReadToEnd();
            var body = JsonConvert.DeserializeObject<TokenPair>(jsonBody);
            ctx.Request.Body.Position = 0;
            if (body != null && body.Token != null)
            {
                return body.Token;
            }
        }
        return string.Empty;
    }

    public async Task InvokeAsync(HttpContext context,
        ITokenLocker tokenLocker
        )
    {
        var ctx = context;
        if (tokenLocker.IsBlacklisted(GetToken(ctx)))
        {
            int statusCode = (int)HttpStatusCode.Unauthorized;
            ctx.Response.StatusCode = statusCode;
            var response = FaziHttpResponse.Create(statusCode, "Unauthorized: Invalid / Expired token");
            await context.Response.WriteAsync(JsonConvert.SerializeObject(response));
            return;
        }

        await _next(context);
    }
}

【讨论】:

    猜你喜欢
    • 2016-12-03
    • 2020-04-17
    • 2021-04-13
    • 2021-09-25
    • 2015-05-06
    • 2019-03-19
    • 2022-07-08
    • 2016-12-23
    相关资源
    最近更新 更多