[英]Revoke JWT using C#
我有一个自定义Authorize
class,当用户从服务器请求数据或任何内容时,我用它来使令牌无效
但每当令牌过期时,原则仍将IsAuthenticated
返回为 true,并且仍会调用控制器并获取数据。
我想要它做的是使令牌无效并显式地将用户从系统中注销。 我找不到任何有用的东西。 如果需要,我可以提供 JWT 属性/过滤器的代码
更新 1:令牌生成
public static string GenerateToken(User user)
{
int expireMinutes;
try
{
expireMinutes = string.IsNullOrEmpty(ConfigurationManager.AppSettings["SessionTimeInMinutes"])
? 30
: int.Parse(ConfigurationManager.AppSettings["SessionTimeInMinutes"]);
}
catch (Exception)
{
expireMinutes = 30;
}
var symmetricKey = Convert.FromBase64String(Secret);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.Now;
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Email, user.Email)
,new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName)
,new Claim("uid", user.Id.ToString())
,new Claim("cid", user.ClientId.ToString())
,new Claim("rid", string.Join(",", user.Roles.Select(r => r.RoleId).ToList()))
}),
Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
IssuedAt = now,
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
};
var stoken = tokenHandler.CreateToken(tokenDescriptor);
var token = tokenHandler.WriteToken(stoken);
return token;
}
服务器端授权令牌
public async Task AuthenticateAsync(
HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var excludedList = new List<string>();
excludedList.Add("/api/Security/IsMustChangePassword");
excludedList.Add("/api/Security/IsTwoFactorEnabled");
if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
{
var request = context.Request;
var authorization = request.Headers.Authorization;
if (authorization == null || authorization.Scheme != "Token")
{
return;
}
if (string.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing Jwt Token", request);
return;
}
//{
// context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
// return;
//}
var token = authorization.Parameter;
var principal = await AuthenticateJwtToken(token).ConfigureAwait(true);
var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
var accountManager = new AccountManager();
var user = accountManager.GetUserDetails(userId);
var newToken = JwtManager.GenerateToken(user);
if (principal == null)
context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
else
context.Principal = principal;
if (principal.Identity.IsAuthenticated)
{
var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
if ((DateTime.Now - expiryDate).TotalSeconds > 0)
{
context.Request.Headers.Authorization = null;
context.Request.RequestUri = null;
}
else
{
var authorize = new AuthenticationHeaderValue("token", newToken);
context.Request.Headers.Authorization = authorize;
context.ActionContext.Request.Headers.Authorization = authorization;
}
}
}
}
private static bool ValidateToken(string token, out string username, out
string passwordHash)
{
username = null;
passwordHash = null;
try
{
var principle = JwtManager.GetPrincipal(token);
var identity = principle.Identity as ClaimsIdentity;
if (identity == null)
return false;
if (!identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
var passwordClaim = identity.FindFirst(ClaimTypes.Hash);
username = usernameClaim?.Value;
passwordHash = passwordClaim?.Value;
return !string.IsNullOrEmpty(username);
var user = identity.FindFirst(username);
return (user != null);
//return (user != null && user.PasswordHash == passwordHash);
}
catch (NullReferenceException)
{
return false;
}
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
string passwordHash;
if (!ValidateToken(token, out username, out passwordHash))
return Task.FromResult<IPrincipal>(null);
// based on username to get more information from database in order to build local identity
var claims = new List<Claim> { new Claim(ClaimTypes.Name, username) };
//claims.Add(new Claim(ClaimTypes.Hash, passwordHash));
// Add more claims if needed: Roles, ...
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context,
CancellationToken cancellationToken)
{
var authorization = context.Request.Headers.Authorization;
var excludedList =
new List<string> {
"/api/Security/IsMustChangePassword",
"/api/Security/IsTwoFactorEnabled" };
if (context.Request.Headers.Authorization != null)
{
if (!excludedList.Contains(context.ActionContext.Request.RequestUri.LocalPath))
{
var token = context.Request.Headers.Authorization.Parameter;
var userId = int.Parse(new JwtManager().GetUserIdFromToken(token));
var accountManager = new AccountManager();
var user = accountManager.GetUserDetails(userId);
var newToken = JwtManager.GenerateToken(user);
var expiryDate = JwtManager.GetSecurityToken(token).ValidTo.ToLocalTime();
if ((DateTime.Now - expiryDate).TotalSeconds > 0)
{
context.Request.Headers.Authorization = null;
context.Request.RequestUri = null;
context.Request.RequestUri = new Uri("/Login");
}
else
{
var authorize = new AuthenticationHeaderValue("token", newToken);
context.Request.Headers.Authorization = authorize;
context.ActionContext.Request.Headers.Authorization = authorization;
}
Challenge(context);
}
}
else
{
var req = context.Request.RequestUri;
var url = context.Request.RequestUri = new Uri($"http://{req.Host}:{req.Port}/api/Security/login");
context.Request.RequestUri = url;
context.Request.Headers.Authorization = null;
context.Result= new AuthenticationFailureResult(string.Empty, new HttpRequestMessage());
}
return Task.FromResult(0);
}
首先:JWT 只是客户端可以操作的客户端令牌。 这里没有绝对的安全。 JWT 由 symmetirc 密钥保护,但不能使其本身失效。 有效令牌在到期之前一直有效。 JWT 的一个缺陷(正如@TimBiegeleisen 在评论中指出的那样),令牌本身不能轻易失效。
如果用户工作时间过长并自动注销,则您的 JWT 已过期并且一切正常。 没有麻烦,因为它自然耗尽,您无需采取行动。
要使令牌无效,您需要使用Expires = now.AddMinutes(-1)
创建一个新令牌。 这样下次您检查 JWT 时会看到它已过期。
用户可以保存 JWT 并在注销后使用它的情况只能通过将 JWT 列入黑名单或另外维护某种其他类型的服务器端会话来实现(这不适用于无状态 Web 服务)。
编辑:删除了错误信息并添加了一种使 JWT 无效的附加方法(黑名单)
您可以简单地兑现您想要撤销的令牌,然后让您的身份验证部分将已撤销的请求与现金中的请求进行比较,并根据该请求拒绝请求
直到代币到期,现金应该知道代币的重新开采时间并兑现那么长时间
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.