97 lines
3.1 KiB
C#
97 lines
3.1 KiB
C#
using Microsoft.Extensions.Caching.Memory;
|
|
using OnlyOneAccessTemplate.Models.RateLimits;
|
|
|
|
namespace OnlyOneAccessTemplate.Services
|
|
{
|
|
public class RateLimitService : IRateLimitService
|
|
{
|
|
private readonly IMemoryCache _cache;
|
|
private readonly IConfiguration _configuration;
|
|
private const int MAX_REQUESTS_PER_HOUR = 50;
|
|
private const int CAPTCHA_THRESHOLD = 20;
|
|
|
|
public RateLimitService(IMemoryCache cache, IConfiguration configuration)
|
|
{
|
|
_cache = cache;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
public async Task<bool> ShouldShowCaptchaAsync(string ipAddress)
|
|
{
|
|
var key = $"rate_limit_{ipAddress}";
|
|
if (_cache.TryGetValue(key, out RateLimitEntry entry))
|
|
{
|
|
// Resetar contador se passou mais de 1 hora
|
|
if (DateTime.UtcNow.Subtract(entry.LastRequest).TotalHours > 1)
|
|
{
|
|
entry.RequestCount = 0;
|
|
}
|
|
|
|
return entry.RequestCount >= CAPTCHA_THRESHOLD;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public async Task RecordRequestAsync(string ipAddress)
|
|
{
|
|
var key = $"rate_limit_{ipAddress}";
|
|
if (_cache.TryGetValue(key, out RateLimitEntry entry))
|
|
{
|
|
entry.RequestCount++;
|
|
entry.LastRequest = DateTime.UtcNow;
|
|
}
|
|
else
|
|
{
|
|
entry = new RateLimitEntry
|
|
{
|
|
IpAddress = ipAddress,
|
|
RequestCount = 1,
|
|
LastRequest = DateTime.UtcNow
|
|
};
|
|
}
|
|
|
|
_cache.Set(key, entry, TimeSpan.FromHours(2));
|
|
}
|
|
|
|
public async Task<(bool IsValid, string Challenge)> GenerateCaptchaAsync()
|
|
{
|
|
// Captcha matemático simples
|
|
var random = new Random();
|
|
var num1 = random.Next(1, 10);
|
|
var num2 = random.Next(1, 10);
|
|
var operation = random.Next(0, 2) == 0 ? "+" : "-";
|
|
|
|
var challenge = $"{num1} {operation} {num2}";
|
|
var answer = operation == "+" ? num1 + num2 : num1 - num2;
|
|
|
|
var challengeKey = Guid.NewGuid().ToString();
|
|
_cache.Set($"captcha_{challengeKey}", answer, TimeSpan.FromMinutes(10));
|
|
|
|
return (true, $"{challenge}|{challengeKey}");
|
|
}
|
|
|
|
public async Task<bool> ValidateCaptchaAsync(string challenge, string response)
|
|
{
|
|
if (string.IsNullOrEmpty(challenge) || string.IsNullOrEmpty(response))
|
|
return false;
|
|
|
|
var parts = challenge.Split('|');
|
|
if (parts.Length != 2) return false;
|
|
|
|
var challengeKey = parts[1];
|
|
var cacheKey = $"captcha_{challengeKey}";
|
|
|
|
if (_cache.TryGetValue(cacheKey, out int expectedAnswer))
|
|
{
|
|
_cache.Remove(cacheKey);
|
|
if (int.TryParse(response, out int userAnswer))
|
|
{
|
|
return userAnswer == expectedAnswer;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|