WorkCamera/client/WorkCameraExport/Services/LoginStateManager.cs
2026-01-06 00:42:25 +08:00

293 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using WorkCameraExport.Models;
using WorkCameraExport.Services.Interfaces;
namespace WorkCameraExport.Services
{
/// <summary>
/// 登录状态管理器 - 负责管理登录状态、自动登录、登录失效检测
/// </summary>
public class LoginStateManager : IDisposable
{
private readonly ApiService _apiService;
private readonly IConfigService _configService;
private readonly ILogService? _logService;
private readonly AppSettings _settings;
private System.Threading.Timer? _tokenCheckTimer;
private bool _disposed;
// Token 检查间隔5分钟
private const int TokenCheckIntervalMs = 5 * 60 * 1000;
// Token 即将过期阈值10分钟
private const int TokenExpiryThresholdMinutes = 10;
/// <summary>
/// 登录失效事件
/// </summary>
public event EventHandler<LoginExpiredEventArgs>? LoginExpired;
/// <summary>
/// Token 即将过期事件
/// </summary>
public event EventHandler? TokenExpiringSoon;
/// <summary>
/// 当前登录用户名
/// </summary>
public string? CurrentUsername { get; private set; }
/// <summary>
/// 是否已登录
/// </summary>
public bool IsLoggedIn => _apiService.IsLoggedIn;
public LoginStateManager(
ApiService apiService,
IConfigService configService,
ILogService? logService = null)
{
_apiService = apiService;
_configService = configService;
_logService = logService;
_settings = AppSettings.Load();
}
/// <summary>
/// 尝试自动登录
/// </summary>
/// <returns>是否自动登录成功</returns>
public async Task<bool> TryAutoLoginAsync()
{
// 每次尝试自动登录时重新加载设置,确保获取最新的保存信息
var settings = AppSettings.Load();
_logService?.Info($"[自动登录] 检查设置: RememberMe={settings.RememberMe}, ServerUrl={settings.ServerUrl}, SavedToken长度={settings.SavedToken?.Length ?? 0}");
// 首先检查 AppSettings 中的保存信息
if (!settings.RememberMe ||
string.IsNullOrEmpty(settings.ServerUrl) ||
string.IsNullOrEmpty(settings.SavedToken))
{
_logService?.Info("未找到保存的登录信息,跳过自动登录");
return false;
}
// 检查 Token 是否已过期
if (settings.TokenExpireTime.HasValue &&
settings.TokenExpireTime.Value <= DateTime.Now)
{
_logService?.Info("保存的 Token 已过期,清除登录信息");
ClearLoginState();
return false;
}
_logService?.Info("尝试自动登录...");
try
{
_apiService.SetBaseUrl(settings.ServerUrl);
_apiService.SetToken(settings.SavedToken);
// 验证 Token 是否有效(通过获取当前用户信息)
var (success, message, userInfo) = await _apiService.GetCurrentUserAsync();
if (success && userInfo != null)
{
CurrentUsername = userInfo.UserName ?? settings.Username;
StartTokenCheckTimer();
_logService?.Info($"自动登录成功,用户: {CurrentUsername}");
return true;
}
_logService?.Warn($"Token 验证失败: {message},清除登录信息");
ClearLoginState();
return false;
}
catch (Exception ex)
{
_logService?.Error($"自动登录异常: {ex.Message}", ex);
ClearLoginState();
return false;
}
}
/// <summary>
/// 登录成功后调用,保存登录状态
/// </summary>
public void OnLoginSuccess(string serverUrl, string username, string token, bool rememberMe)
{
CurrentUsername = username;
_settings.ServerUrl = serverUrl;
_settings.Username = username;
_settings.RememberMe = rememberMe;
if (rememberMe)
{
_settings.SavedToken = token;
_settings.TokenExpireTime = DateTime.Now.AddDays(7);
_configService.SaveCredentials(serverUrl, token, username);
}
else
{
_settings.SavedToken = "";
_settings.TokenExpireTime = null;
_configService.ClearCredentials();
}
_settings.Save();
StartTokenCheckTimer();
_logService?.Info($"登录成功,用户: {username},记住登录: {rememberMe}");
}
/// <summary>
/// 自动登录成功后调用,仅启动定时器,不重新保存凭证
/// </summary>
public void OnAutoLoginSuccess(string username)
{
CurrentUsername = username;
StartTokenCheckTimer();
_logService?.Info($"自动登录成功,用户: {username}");
}
/// <summary>
/// 退出登录
/// </summary>
public void Logout()
{
_logService?.Info($"用户 {CurrentUsername} 退出登录");
StopTokenCheckTimer();
_apiService.Logout();
ClearLoginState();
CurrentUsername = null;
}
/// <summary>
/// 清除登录状态
/// </summary>
public void ClearLoginState()
{
_settings.ClearLoginInfo();
_configService.ClearCredentials();
}
/// <summary>
/// 检查登录状态是否有效
/// </summary>
public async Task<bool> CheckLoginStateAsync()
{
if (!_apiService.IsLoggedIn)
{
return false;
}
try
{
// 通过获取当前用户信息验证 Token 有效性
var (success, _, _) = await _apiService.GetCurrentUserAsync();
if (!success)
{
OnLoginExpired("登录已失效,请重新登录");
return false;
}
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 启动 Token 检查定时器
/// </summary>
private void StartTokenCheckTimer()
{
StopTokenCheckTimer();
_tokenCheckTimer = new System.Threading.Timer(
CheckTokenExpiry,
null,
TokenCheckIntervalMs,
TokenCheckIntervalMs);
}
/// <summary>
/// 停止 Token 检查定时器
/// </summary>
private void StopTokenCheckTimer()
{
_tokenCheckTimer?.Dispose();
_tokenCheckTimer = null;
}
/// <summary>
/// 检查 Token 是否即将过期
/// </summary>
private void CheckTokenExpiry(object? state)
{
if (_settings.TokenExpireTime.HasValue)
{
var timeUntilExpiry = _settings.TokenExpireTime.Value - DateTime.Now;
if (timeUntilExpiry.TotalMinutes <= 0)
{
// Token 已过期
OnLoginExpired("登录已过期,请重新登录");
}
else if (timeUntilExpiry.TotalMinutes <= TokenExpiryThresholdMinutes)
{
// Token 即将过期,尝试刷新
_logService?.Info("Token 即将过期,触发刷新事件");
TokenExpiringSoon?.Invoke(this, EventArgs.Empty);
}
}
}
/// <summary>
/// 触发登录失效事件
/// </summary>
private void OnLoginExpired(string reason)
{
_logService?.Warn($"登录失效: {reason}");
StopTokenCheckTimer();
LoginExpired?.Invoke(this, new LoginExpiredEventArgs(reason));
}
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
StopTokenCheckTimer();
}
_disposed = true;
}
}
#endregion
}
/// <summary>
/// 登录失效事件参数
/// </summary>
public class LoginExpiredEventArgs : EventArgs
{
public string Reason { get; }
public LoginExpiredEventArgs(string reason)
{
Reason = reason;
}
}
}