我有一个自定义的Authorize
类,当用户向服务器请求数据或任何东西时,我使用该类使令牌无效
,但是每当令牌到期时,原理仍然返回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);
}
答案 0 :(得分:-1)
首先:JWT只是客户端可以操作的客户端令牌。这里没有绝对的安全性。 JWT由对称密钥保护,但本身不能失效。有效令牌在过期之前一直保持有效。 JWT的一个缺陷(如@TimBiegeleisen在评论中指出的那样)是,令牌本身不能轻易失效。
如果用户工作时间太长并自动注销,则您的JWT已过期,一切都很好。没有麻烦,因为它会自然耗尽,您无需采取任何行动。
要使令牌无效,您需要使用Expires = now.AddMinutes(-1)
创建一个新令牌。这样,下次下次检查JWT时,它就会过期。
只有在将JWT列入黑名单或永久维护某种其他服务器端会话的情况下,用户才可以保存JWT并在注销后仍使用它的情况(这不适用于无状态Web服务)。
编辑:删除了错误的信息,并添加了使JWT(黑名单)无效的其他方法