feat(auth): 实现JWT令牌黑名单机制
- 在退出登录时清除JWT Token Cookie并将其加入黑名单 - 添加JTI声明到JWT令牌用于唯一标识和黑名单管理 - 在中间件中验证令牌时检查黑名单状态并拒绝已注销的令牌 - 实现令牌立即失效功能防止已退出用户继续访问系统
This commit is contained in:
@@ -56,9 +56,16 @@ namespace YLErp.Helpers
|
||||
{
|
||||
var key = new SymmetricSecurityKey(Convert.FromBase64String(JwtSecretKey));
|
||||
|
||||
// 添加 JTI (JWT ID) 用于黑名单机制
|
||||
var claims = new ClaimsIdentity(identity);
|
||||
if (!claims.HasClaim(c => c.Type == "jti"))
|
||||
{
|
||||
claims.AddClaim(new Claim("jti", Guid.NewGuid().ToString("N")));
|
||||
}
|
||||
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(identity),
|
||||
Subject = claims,
|
||||
Expires = DateTime.UtcNow.AddSeconds(ExpireInSeconds),
|
||||
IssuedAt = DateTime.Now,
|
||||
Issuer = JwtIssuer,
|
||||
|
||||
@@ -311,6 +311,35 @@ namespace YLErp.Web.Controllers
|
||||
HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
|
||||
logger.Info($"[退出登录] SignOutAsync 已调用");
|
||||
|
||||
// 清除 JWT Token Cookie
|
||||
Response.Cookies.Delete("Access-Token");
|
||||
Response.Cookies.Delete("Access-Token-Encrypt");
|
||||
logger.Info($"[退出登录] JWT Token Cookie 已清除");
|
||||
|
||||
// 将当前 JWT Token 加入黑名单(使其立即失效)
|
||||
if (Request.Cookies.TryGetValue("Access-Token", out var accessToken) && !string.IsNullOrEmpty(accessToken))
|
||||
{
|
||||
try
|
||||
{
|
||||
var tokenHandler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
|
||||
var jwtToken = tokenHandler.ReadJwtToken(accessToken);
|
||||
var jti = jwtToken.Claims.FirstOrDefault(c => c.Type == "jti")?.Value ?? accessToken.Substring(0, 32);
|
||||
var expiry = jwtToken.ValidTo;
|
||||
var ttl = expiry - DateTime.UtcNow;
|
||||
|
||||
if (ttl.TotalSeconds > 0)
|
||||
{
|
||||
// 将 Token 加入黑名单,过期时间与 Token 过期时间相同
|
||||
Server.CacheProvider.Set($"token_blacklist:{jti}", userId.ToString(), ttl);
|
||||
logger.Info($"[退出登录] Token 已加入黑名单 - JTI: {jti}, 过期时间: {expiry:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error($"[退出登录] Token 加入黑名单失败", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (SsoLoginConfig.SSO_Enable)
|
||||
{
|
||||
switch (loginName?.ToLowerInvariant())
|
||||
|
||||
@@ -54,6 +54,22 @@ namespace YLErp.Web.Middleware
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
|
||||
// 先解析token获取JTI,检查是否在黑名单中
|
||||
var jwtTokenForCheck = tokenHandler.ReadJwtToken(token);
|
||||
var jti = jwtTokenForCheck.Claims.FirstOrDefault(c => c.Type == "jti")?.Value ?? token.Substring(0, 32);
|
||||
|
||||
// 检查token是否在黑名单中
|
||||
var blacklisted = Server.CacheProvider.Get($"token_blacklist:{jti}");
|
||||
if (blacklisted != null)
|
||||
{
|
||||
LogFactory.GetLogger("RefreshTokenMiddleware").Info($"[Token验证] Token已在黑名单中,拒绝访问 - JTI: {jti}");
|
||||
context.Response.Cookies.Delete("Access-Token");
|
||||
context.Response.Cookies.Delete("Access-Token-Encrypt");
|
||||
context.Response.StatusCode = 401;
|
||||
await context.Response.WriteAsync("Token已失效,请重新登录");
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证并解析token
|
||||
var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
|
||||
var jwtToken = validatedToken as JwtSecurityToken;
|
||||
|
||||
Reference in New Issue
Block a user