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