diff --git a/src/Libraries/Nop.Core/Configuration/CommonConfig.cs b/src/Libraries/Nop.Core/Configuration/CommonConfig.cs
index d818260ab90..770cce63303 100644
--- a/src/Libraries/Nop.Core/Configuration/CommonConfig.cs
+++ b/src/Libraries/Nop.Core/Configuration/CommonConfig.cs
@@ -70,4 +70,11 @@ public partial class CommonConfig : IConfig
/// Default status code to set on the response when a request is rejected.
///
public int RejectionStatusCode { get; set; } = 503;
+
+ ///
+ /// Gets or sets the collection of user identifiers that are exempt from rate limiting.
+ ///
+ /// Add user IDs or IP Addresses to this collection to allow them to bypass rate limit checks. Changes to the
+ /// collection take effect immediately.
+ public HashSet RateLimitWhitelist { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs b/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs
index 17d4e228f95..2d9b0214002 100644
--- a/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs
+++ b/src/Presentation/Nop.Web.Framework/Infrastructure/Extensions/ServiceCollectionExtensions.cs
@@ -14,6 +14,7 @@
using Nop.Core;
using Nop.Core.Configuration;
using Nop.Core.Domain.Common;
+using Nop.Core.Domain.Security;
using Nop.Core.Http;
using Nop.Core.Infrastructure;
using Nop.Core.Security;
@@ -118,17 +119,32 @@ public static void ConfigureApplicationServices(this IServiceCollection services
builder.Services.AddRateLimiter(options =>
{
var settings = Singleton.Instance.Get();
+ var securitySettings = Singleton.Instance;
options.GlobalLimiter = PartitionedRateLimiter.Create(httpContext =>
- RateLimitPartition.GetFixedWindowLimiter(
- partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(),
+ {
+ var username = httpContext.User.Identity?.Name;
+ var ip = httpContext.Connection.RemoteIpAddress?.ToString();
+
+ if (settings.RateLimitWhitelist.Contains(ip) || //allow from configured IP addresses
+ securitySettings.AdminAreaAllowedIpAddresses.Contains(ip) || //allow from admin area allowed IP addresses
+ httpContext.Connection.LocalIpAddress == httpContext.Connection.RemoteIpAddress) //allow from localhost
+ {
+ return RateLimitPartition.GetNoLimiter("");
+ }
+
+ var partitionKey = username ?? ip ?? httpContext.Session?.Id ?? httpContext.Request.Headers.Host;
+
+ return RateLimitPartition.GetFixedWindowLimiter(
+ partitionKey: partitionKey,
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = settings.PermitLimit,
QueueLimit = settings.QueueCount,
Window = TimeSpan.FromMinutes(1)
- }));
+ });
+ });
options.RejectionStatusCode = settings.RejectionStatusCode;
});