使用滑动窗口实现登录失败次数限制并锁定登录是一种常见的安全策略,它可以有效防止暴力破解攻击
UserLoginTracker类实现了登录尝试的追踪和限制功能。
它通过记录登录尝试的时间戳,并使用一个滑动窗口来限制在一定时间窗口内的登录尝试次数。如果登录尝试次数超过了设定的最大值,账户将被锁定一段时间
根据实际需求调整
window_size(窗口大小,即限制的时间范围)
max_attempts(最大尝试次数)
lockout_duration(锁定持续时间)
public class UserLoginTracker
{
private readonly int _windowSize;
private readonly int _maxAttempts;
private readonly int _lockoutDuration;
private readonly Dictionary<string, Queue<DateTime>> _userAttempts;
private readonly Dictionary<string, DateTime> _userLockedUntil;
/// <summary>
/// 用户登录跟踪器
/// </summary>
/// <param name="windowSize">限制时间窗口的大小,单位是秒</param>
/// <param name="maxAttempts">允许的最大尝试次数</param>
/// <param name="lockoutDuration">账户锁定的持续时间,单位也是秒</param>
public UserLoginTracker(int windowSize, int maxAttempts, int lockoutDuration)
{
_windowSize = windowSize;
_maxAttempts = maxAttempts;
_lockoutDuration = lockoutDuration;
_userAttempts = new Dictionary<string, Queue<DateTime>>();
_userLockedUntil = new Dictionary<string, DateTime>();
}
public (int, TimeSpan) LoginAttempt(string username)
{
var currentTime = DateTime.Now;
var remainingAttempts = 0;
var timeUntilUnlock = TimeSpan.Zero;
// 如果用户已经被锁定,检查是否可以解锁
if (_userLockedUntil.ContainsKey(username) && currentTime < _userLockedUntil[username])
{
Console.WriteLine("账户已锁定,请稍后再试");
timeUntilUnlock = _userLockedUntil[username] - currentTime;
return (remainingAttempts, timeUntilUnlock);
}
// 初始化用户尝试记录
if (!_userAttempts.ContainsKey(username))
{
_userAttempts[username] = new Queue<DateTime>(_windowSize);
}
// 清理超过时间窗口的登录尝试
CleanupAttempts(username, currentTime);
// 记录登录尝试
_userAttempts[username].Enqueue(currentTime);
remainingAttempts = _maxAttempts - _userAttempts[username].Count;
// 检查登录尝试次数是否超过限制
if (_userAttempts[username].Count < _maxAttempts) return (remainingAttempts, timeUntilUnlock);
Console.WriteLine("登录尝试次数过多,账户已被锁定");
_userLockedUntil[username] = currentTime.AddSeconds(_lockoutDuration);
timeUntilUnlock = _userLockedUntil[username] - currentTime;
remainingAttempts = 0;
return (remainingAttempts, timeUntilUnlock);
}
private void CleanupAttempts(string username, DateTime currentTime)
{
if (!_userAttempts.ContainsKey(username))
return;
while (_userAttempts[username].Count > 0 && currentTime - _userAttempts[username].Peek() > TimeSpan.FromSeconds(_windowSize))
{
_userAttempts[username].Dequeue();
}
}
}
public class Program
{
static void Main(string[] args)
{
var tracker = new UserLoginTracker(windowSize: 5, maxAttempts: 3, lockoutDuration: 60);
for (var i = 0; i < 80; i++)
{
var (remainingAttempts, timeUntilUnlock) = tracker.LoginAttempt("user1");
Console.WriteLine(remainingAttempts == 0
? $"账户已被锁定,解锁时间:{DateTime.Now.Add(timeUntilUnlock)}"
: $"剩余尝试次数:{remainingAttempts}");
}
}
}