fix(auth): 解决登录登出流程中的Cookie管理和令牌验证问题

- 在登录页面访问时记录所有Cookie信息并优化清除逻辑
- 登录处理时添加详细的请求日志记录
- 登出时先删除可能存在的旧Token避免黑名单影响
- 完善登出时的Cookie清除机制,清除所有Cookie确保安全
- 优化中间件中的令牌黑名单检查和异常处理
- 添加响应头确保浏览器正确清除缓存
- 改进日志记录的详细程度便于问题排查
This commit is contained in:
hjhan
2026-03-24 14:09:20 +08:00
parent 9be15621f5
commit ce842703fb
2 changed files with 46 additions and 13 deletions
+33 -7
View File
@@ -36,15 +36,21 @@ namespace YLErp.Web.Controllers
{
var logger = LogFactory.GetLogger<AccountController>();
var remoteIp = HttpContext.Connection.RemoteIpAddress?.ToString();
logger.Info($"[登录页面] 访问登录页,IP: {remoteIp}, skipSso: {skipSso}");
// 记录所有 Cookie
var allCookies = Request.Cookies.Select(c => $"{c.Key}={(c.Value?.Length > 10 ? c.Value?.Substring(0, 10) + "..." : c.Value)}").ToList();
var hasAccessToken = Request.Cookies.ContainsKey("Access-Token");
logger.Info($"[登录页面] 访问登录页,IP: {remoteIp}, skipSso: {skipSso}, HasAccessToken: {hasAccessToken}, Cookies: [{string.Join(", ", allCookies)}]");
var keys = Request.Cookies.Keys.ToArray();
logger.Info($"[登录页面] 清除 {keys.Length} 个 Cookie(保留 {_auth_login}");
foreach (var key in keys)
{
if (key != _auth_login)
{
Response.Cookies.Delete(key);
logger.Info($"[登录页面] 删除 Cookie: {key}");
}
}
@@ -113,8 +119,13 @@ namespace YLErp.Web.Controllers
[HttpPost, ResponseCache(NoStore = true)]
public async Task<IActionResult> LoginHandle(LogOnModel model)
{
var logger = LogFactory.GetLogger<AccountController>();
var hasToken = Request.Cookies.ContainsKey("Access-Token");
logger.Info($"[登录处理] 收到登录请求 - UserName: {model?.UserName}, HasAccessToken: {hasToken}");
if (string.IsNullOrWhiteSpace(model?.UserName) || string.IsNullOrWhiteSpace(model?.Password))
{
logger.Info("[登录处理] 用户名或密码为空");
return LoginError("用户名和密码必须填写");
}
@@ -249,6 +260,10 @@ namespace YLErp.Web.Controllers
await HttpContext.SignInAsync(AuthHelper.CookieAuthType, new ClaimsPrincipal(identity));
// 先删除可能存在的旧Token(避免黑名单影响)
Response.Cookies.Delete("Access-Token");
Response.Cookies.Delete("Access-Token-Encrypt");
var jwtToken = AuthHelper.CreateJwtToken2(identity);
Response.Cookies.Append("Access-Token", jwtToken, new CookieOptions
{
@@ -274,7 +289,9 @@ namespace YLErp.Web.Controllers
var loginName = User.GetUserName();
var remoteIp = HttpContext.Connection.RemoteIpAddress?.ToString();
logger.Info($"[退出登录] 用户请求退出 - UserId: {userId}, LoginName: {loginName}, IP: {remoteIp}");
// 记录所有 Cookie
var allCookies = Request.Cookies.Select(c => $"{c.Key}={c.Value?.Substring(0, Math.Min(10, c.Value?.Length ?? 0))}...").ToList();
logger.Info($"[退出登录] 用户请求退出 - UserId: {userId}, LoginName: {loginName}, IP: {remoteIp}, Cookies: [{string.Join(", ", allCookies)}]");
if (userId > 0)
{
@@ -336,10 +353,13 @@ namespace YLErp.Web.Controllers
}
}
// 清除 JWT Token Cookie(在加入黑名单之后
Response.Cookies.Delete("Access-Token");
Response.Cookies.Delete("Access-Token-Encrypt");
logger.Info($"[退出登录] JWT Token Cookie 已清除");
// 清除所有 Cookie(确保旧Token不会带到登录页面
var keys = Request.Cookies.Keys.ToArray();
foreach (var key in keys)
{
Response.Cookies.Delete(key);
}
logger.Info($"[退出登录] 所有 Cookie 已清除,共 {keys.Length} 个");
if (SsoLoginConfig.SSO_Enable)
{
@@ -356,7 +376,13 @@ namespace YLErp.Web.Controllers
}
}
logger.Info($"[退出登录] 跳转到登录页");
logger.Info($"[退出登录] 准备重定向到登录页,清除的Cookie数量: {keys.Length}");
// 添加响应头,确保浏览器清除Cookie
Response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
Response.Headers.Add("Pragma", "no-cache");
Response.Headers.Add("Expires", "0");
return RedirectToAction("Login");
}
+13 -6
View File
@@ -36,25 +36,32 @@ namespace YLErp.Web.Middleware
{
// 获取当前请求路径
var path = context.Request.Path.Value?.ToLowerInvariant() ?? "";
var logger = LogFactory.GetLogger("RefreshTokenMiddleware");
// 记录请求信息
var hasToken = context.Request.Cookies.TryGetValue("Access-Token", out var token);
var tokenPreview = hasToken && token.Length > 20 ? token.Substring(0, 20) + "..." : token;
logger.Info($"[中间件] 请求: {path}, HasToken: {hasToken}, TokenPreview: {tokenPreview}");
// 跳过登录相关页面和静态资源,避免黑名单Token影响登录流程
// 注意:/account/login 包含 /account/loginhandle (POST登录请求)
if (path.StartsWith("/account/login") ||
path.StartsWith("/account/logout") ||
path.StartsWith("/account/captcha") ||
path.StartsWith("/account/sso") ||
path.StartsWith("/account/callback") ||
path.StartsWith("/account/callback") || // SSO回调地址
path.StartsWith("/content/") ||
path.StartsWith("/scripts/") ||
path.StartsWith("/css/") ||
path.StartsWith("/images/") ||
path.StartsWith("/favicon.ico"))
{
logger.Info($"[中间件] 白名单跳过: {path}");
await _next(context);
return;
}
// 检查是否有Access-Token
if (context.Request.Cookies.TryGetValue("Access-Token", out var token))
// 检查是否有Access-Token(使用已声明的token变量)
if (hasToken)
{
try
{
@@ -79,9 +86,10 @@ namespace YLErp.Web.Middleware
// 检查token是否在黑名单中
var blacklisted = Server.CacheProvider.Get($"token_blacklist:{jti}");
logger.Info($"[中间件] 黑名单检查 - JTI: {jti}, Blacklisted: {blacklisted != null}");
if (blacklisted != null)
{
LogFactory.GetLogger("RefreshTokenMiddleware").Info($"[Token验证] Token在黑名单中,拒绝访问 - JTI: {jti}");
logger.Info($"[中间件] Token在黑名单中,拒绝访问 - JTI: {jti}, Path: {path}");
context.Response.Cookies.Delete("Access-Token");
context.Response.Cookies.Delete("Access-Token-Encrypt");
context.Response.StatusCode = 401;
@@ -162,7 +170,6 @@ namespace YLErp.Web.Middleware
catch (Exception ex)
{
// 记录其他异常
var logger = LogFactory.GetLogger("RefreshTokenMiddleware");
logger.Error($"[Token刷新] 未知错误 - Token: {token?.Substring(0, Math.Min(20, token?.Length ?? 0))}..., 异常类型: {ex.GetType().Name}", ex);
Console.WriteLine($"Token refresh error: {ex.Message}");
// 继续处理请求,不影响正常流程