71 lines
2.3 KiB
C#
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);
|
|
}
|