如何禁用/禁用JWT令牌?

时间:2019-10-11 14:02:55

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

这就是我为.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;
}

1 个答案:

答案 0 :(得分:0)

最后,我要做的是创建中间件,并将其放在管道的顶部。我拿着一个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);
    }
}