Files
petwash/PetWashControl/Services/AdminAccessService.cs
GukSang.Jin 0a884fa6cb 更新
2026-03-18 13:53:44 +08:00

71 lines
2.3 KiB
C#

namespace PetWashControl.Services;
public sealed class AdminAccessService
{
private readonly ConfigurationService _config;
private int _failedAttempts;
private DateTimeOffset? _lockedUntil;
public AdminAccessService(ConfigurationService config)
{
_config = config;
}
public AdminAuthResult TryAuthenticate(string username, string password)
{
if (_lockedUntil is { } lockedUntil && lockedUntil > DateTimeOffset.Now)
{
return AdminAuthResult.Locked(GetLockoutMessage(lockedUntil));
}
if (username == _config.AdminUsername && password == _config.AdminPassword)
{
_failedAttempts = 0;
_lockedUntil = null;
return AdminAuthResult.Success();
}
_failedAttempts++;
var maxAttempts = Math.Max(1, _config.AdminMaxFailedAttempts);
if (_failedAttempts >= maxAttempts)
{
_failedAttempts = 0;
_lockedUntil = DateTimeOffset.Now.AddMinutes(Math.Max(1, _config.AdminLockoutMinutes));
return AdminAuthResult.Locked(GetLockoutMessage(_lockedUntil.Value));
}
var remainingAttempts = maxAttempts - _failedAttempts;
return AdminAuthResult.Failed($"账号或密码错误,还可尝试 {remainingAttempts} 次。");
}
public string? GetCurrentLockoutMessage()
{
if (_lockedUntil is not { } lockedUntil || lockedUntil <= DateTimeOffset.Now)
{
_lockedUntil = null;
return null;
}
return GetLockoutMessage(lockedUntil);
}
private static string GetLockoutMessage(DateTimeOffset lockedUntil)
{
var remaining = lockedUntil - DateTimeOffset.Now;
if (remaining < TimeSpan.Zero)
{
remaining = TimeSpan.Zero;
}
var minutes = Math.Max(0, (int)Math.Ceiling(remaining.TotalMinutes));
return $"登录失败次数过多,请 {minutes} 分钟后再试。";
}
}
public readonly record struct AdminAuthResult(bool IsSuccess, bool IsLocked, string Message)
{
public static AdminAuthResult Success() => new(true, false, string.Empty);
public static AdminAuthResult Failed(string message) => new(false, false, message);
public static AdminAuthResult Locked(string message) => new(false, true, message);
}