489 lines
17 KiB
C#
489 lines
17 KiB
C#
#define JWT // FIRST ADMIN FIXED SLIDING CONCUR TOKEN FIXED2 JWT
|
|
#if NEVER
|
|
#elif FIXED
|
|
// <snippet_fixed>
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using System.Threading.RateLimiting;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
var app = builder.Build();
|
|
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddFixedWindowLimiter(policyName: "fixed",
|
|
options =>
|
|
{
|
|
options.PermitLimit = 4;
|
|
options.Window = TimeSpan.FromSeconds(12);
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = 2;
|
|
}));
|
|
|
|
app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}"))
|
|
.RequireRateLimiting("fixed");
|
|
|
|
app.Run();
|
|
// </snippet_fixed>
|
|
#elif FIXED2
|
|
// <snippet_fixed2>
|
|
using System.Threading.RateLimiting;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
builder.Services.Configure<MyRateLimitOptions>(
|
|
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit));
|
|
var app = builder.Build();
|
|
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
var fixedPolicy = "fixed";
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddFixedWindowLimiter(policyName: fixedPolicy,
|
|
options =>
|
|
{
|
|
options.PermitLimit = myOptions.permitLimit;
|
|
options.Window = TimeSpan.FromSeconds(myOptions.window);
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = myOptions.queueLimit;
|
|
}));
|
|
|
|
app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}"))
|
|
.RequireRateLimiting(fixedPolicy);
|
|
|
|
app.Run();
|
|
// </snippet_fixed2>
|
|
#elif SLIDING
|
|
// <snippet_slide>
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using System.Threading.RateLimiting;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
var app = builder.Build();
|
|
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
var slidingPolicy = "sliding";
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddSlidingWindowLimiter(policyName: slidingPolicy,
|
|
options =>
|
|
{
|
|
options.PermitLimit = myOptions.permitLimit;
|
|
options.Window = TimeSpan.FromSeconds(myOptions.window);
|
|
options.SegmentsPerWindow = myOptions.segmentsPerWindow;
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = myOptions.queueLimit;
|
|
}));
|
|
|
|
app.MapGet("/", () => Results.Ok($"Hello {GetTicks()}"))
|
|
.RequireRateLimiting(slidingPolicy);
|
|
|
|
app.Run();
|
|
// </snippet_slide>
|
|
#elif CONCUR
|
|
// Quicktest 10 users, 9 seconds -> 982 requests, 900 errors
|
|
// <snippet_concur>
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using System.Threading.RateLimiting;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
var app = builder.Build();
|
|
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
var concurrencyPolicy = "Concurrency";
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddConcurrencyLimiter(policyName: concurrencyPolicy,
|
|
options =>
|
|
{
|
|
options.PermitLimit = myOptions.permitLimit;
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = myOptions.queueLimit;
|
|
}));
|
|
|
|
app.MapGet("/", async () =>
|
|
{
|
|
await Task.Delay(500);
|
|
return Results.Ok($"Concurrency Limiter {GetTicks()}");
|
|
|
|
}).RequireRateLimiting(concurrencyPolicy);
|
|
|
|
app.Run();
|
|
// </snippet_concur>
|
|
#elif TOKEN
|
|
// Quicktest 20 users, 20 seconds -> 8965 requests 2,250 errors
|
|
// <snippet_token>
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using System.Threading.RateLimiting;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
var app = builder.Build();
|
|
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
var tokenPolicy = "token";
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddTokenBucketLimiter(policyName: tokenPolicy,
|
|
options =>
|
|
{
|
|
options.TokenLimit = myOptions.tokenLimit;
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = myOptions.queueLimit;
|
|
options.ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.replenishmentPeriod);
|
|
options.TokensPerPeriod = myOptions.tokensPerPeriod;
|
|
options.AutoReplenishment = myOptions.autoReplenishment;
|
|
}));
|
|
|
|
app.MapGet("/", () => Results.Ok($"Token Limiter {GetTicks()}"))
|
|
.RequireRateLimiting(tokenPolicy);
|
|
|
|
app.Run();
|
|
// </snippet_token>
|
|
#elif FIRST
|
|
// <snippet_1>
|
|
using System.Globalization;
|
|
using System.Net;
|
|
using System.Threading.RateLimiting;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using WebRateLimitAuth;
|
|
using WebRateLimitAuth.Data;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ??
|
|
throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(connectionString));
|
|
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
|
|
|
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
|
.AddEntityFrameworkStores<ApplicationDbContext>();
|
|
|
|
builder.Services.Configure<MyRateLimitOptions>(
|
|
builder.Configuration.GetSection(MyRateLimitOptions.MyRateLimit));
|
|
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddControllersWithViews();
|
|
|
|
var app = builder.Build();
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseMigrationsEndPoint();
|
|
}
|
|
else
|
|
{
|
|
app.UseExceptionHandler("/Error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
app.UseStaticFiles();
|
|
|
|
app.UseRouting();
|
|
|
|
// <snippet>
|
|
// Preceding code removed for brevity.
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
var userPolicyName = "user";
|
|
var helloPolicy = "hello";
|
|
var myOptions = new MyRateLimitOptions();
|
|
var myConfigSection = app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit);
|
|
myConfigSection.Bind(myOptions);
|
|
|
|
var options = new RateLimiterOptions()
|
|
{
|
|
OnRejected = (context, cancellationToken) =>
|
|
{
|
|
if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
|
|
{
|
|
context.HttpContext.Response.Headers.RetryAfter =
|
|
((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
|
|
}
|
|
|
|
context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;
|
|
context?.HttpContext?.RequestServices?.GetService<ILoggerFactory>()?
|
|
.CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware")
|
|
.LogWarning($"OnRejected: {GetUserEndPoint(context.HttpContext)}");
|
|
|
|
return new ValueTask();
|
|
}
|
|
}
|
|
.AddPolicy<string, SampleRateLimiterPolicy>(helloPolicy)
|
|
.AddPolicy<string>(userPolicyName, context =>
|
|
{
|
|
var username = "anonymous user";
|
|
if (context.User?.Identity?.IsAuthenticated is true)
|
|
{
|
|
username = context.User?.ToString()!;
|
|
}
|
|
|
|
return RateLimitPartition.GetSlidingWindowLimiter<string>(username,
|
|
key => new SlidingWindowRateLimiterOptions
|
|
{
|
|
PermitLimit = myOptions.permitLimit,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
Window = TimeSpan.FromSeconds(myOptions.window),
|
|
SegmentsPerWindow = myOptions.segmentsPerWindow
|
|
});
|
|
|
|
});
|
|
|
|
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
|
|
{
|
|
IPAddress? remoteIPaddress = context?.Connection?.RemoteIpAddress;
|
|
|
|
if (!IPAddress.IsLoopback(remoteIPaddress!))
|
|
{
|
|
return RateLimitPartition.GetTokenBucketLimiter<IPAddress>
|
|
(remoteIPaddress!, key =>
|
|
new TokenBucketRateLimiterOptions
|
|
{
|
|
TokenLimit = myOptions.tokenLimit2,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
ReplenishmentPeriod =
|
|
TimeSpan.FromSeconds(myOptions.replenishmentPeriod),
|
|
TokensPerPeriod = myOptions.tokensPerPeriod,
|
|
AutoReplenishment = myOptions.autoReplenishment
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return RateLimitPartition.GetNoLimiter<IPAddress>(IPAddress.Loopback);
|
|
}
|
|
});
|
|
|
|
app.UseRateLimiter(options);
|
|
|
|
app.MapRazorPages().RequireRateLimiting(userPolicyName);
|
|
app.MapDefaultControllerRoute();
|
|
|
|
static string GetUserEndPoint(HttpContext context) =>
|
|
$"User {context.User?.Identity?.Name ?? "Anonymous"} endpoint:{context.Request.Path}"
|
|
+ $" {context.Connection.RemoteIpAddress}";
|
|
static string GetTicks() => (DateTime.Now.Ticks & 0x11111).ToString("00000");
|
|
|
|
app.MapGet("/a", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}")
|
|
.RequireRateLimiting(userPolicyName);
|
|
|
|
app.MapGet("/b", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}")
|
|
.RequireRateLimiting(helloPolicy);
|
|
|
|
app.MapGet("/c", (HttpContext context) => $"{GetUserEndPoint(context)} {GetTicks()}");
|
|
|
|
app.Run();
|
|
// </snippet>
|
|
// </snippet_1>
|
|
#elif ADMIN
|
|
// <snippet_adm>
|
|
using System.Threading.RateLimiting;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Primitives;
|
|
using WebRateLimitAuth.Data;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
|
|
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
options.UseSqlServer(connectionString));
|
|
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
|
|
|
|
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
|
|
options.SignIn.RequireConfirmedAccount = true)
|
|
.AddEntityFrameworkStores<ApplicationDbContext>();
|
|
|
|
builder.Services.AddRazorPages();
|
|
builder.Services.AddControllersWithViews();
|
|
|
|
var app = builder.Build();
|
|
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.UseMigrationsEndPoint();
|
|
}
|
|
else
|
|
{
|
|
app.UseExceptionHandler("/Error");
|
|
app.UseHsts();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
app.UseStaticFiles();
|
|
|
|
app.UseRouting();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
// <snippet_adm2>
|
|
var getPolicyName = "get";
|
|
var postPolicyName = "post";
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
|
|
app.UseRateLimiter(new RateLimiterOptions()
|
|
.AddConcurrencyLimiter(policyName: getPolicyName,
|
|
options =>
|
|
{
|
|
options.PermitLimit = myOptions.permitLimit;
|
|
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
options.QueueLimit = myOptions.queueLimit;
|
|
})
|
|
.AddPolicy(policyName: postPolicyName, partitioner: httpContext =>
|
|
{
|
|
string userName = httpContext?.User?.Identity?.Name ?? string.Empty;
|
|
|
|
if (!StringValues.IsNullOrEmpty(userName))
|
|
{
|
|
return RateLimitPartition.GetTokenBucketLimiter(userName, key =>
|
|
new TokenBucketRateLimiterOptions
|
|
{
|
|
TokenLimit = myOptions.tokenLimit2,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
ReplenishmentPeriod =
|
|
TimeSpan.FromSeconds(myOptions.replenishmentPeriod),
|
|
TokensPerPeriod = myOptions.tokensPerPeriod,
|
|
AutoReplenishment = myOptions.autoReplenishment
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return RateLimitPartition.GetTokenBucketLimiter("Anon", key =>
|
|
new TokenBucketRateLimiterOptions
|
|
{
|
|
TokenLimit = myOptions.tokenLimit,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
ReplenishmentPeriod =
|
|
TimeSpan.FromSeconds(myOptions.replenishmentPeriod),
|
|
TokensPerPeriod = myOptions.tokensPerPeriod,
|
|
AutoReplenishment = true
|
|
});
|
|
}
|
|
}));
|
|
// </snippet_adm2>
|
|
|
|
static string GetUserEndPointMethod(HttpContext context) =>
|
|
$"Hello {context.User?.Identity?.Name ?? "Anonymous"} " +
|
|
$"Endpoint:{context.Request.Path} Method: {context.Request.Method}";
|
|
|
|
app.MapGet("/test", (HttpContext context) => $"{GetUserEndPointMethod(context)}")
|
|
.RequireRateLimiting(getPolicyName);
|
|
|
|
app.MapRazorPages().RequireRateLimiting(getPolicyName)
|
|
.RequireRateLimiting(postPolicyName);
|
|
|
|
app.MapDefaultControllerRoute();
|
|
|
|
app.Run();
|
|
// </snippet_adm>
|
|
#elif JWT
|
|
// <snippet_jwt>
|
|
using System.Threading.RateLimiting;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.RateLimiting;
|
|
using Microsoft.Extensions.Primitives;
|
|
using WebRateLimitAuth.Models;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddAuthorization();
|
|
builder.Services.AddAuthentication("Bearer").AddJwtBearer();
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseAuthorization();
|
|
|
|
var jwtPolicyName = "jwt";
|
|
var myOptions = new MyRateLimitOptions();
|
|
app.Configuration.GetSection(MyRateLimitOptions.MyRateLimit).Bind(myOptions);
|
|
|
|
var options = new RateLimiterOptions()
|
|
{
|
|
RejectionStatusCode = StatusCodes.Status429TooManyRequests
|
|
}
|
|
.AddPolicy(policyName: jwtPolicyName, partitioner: httpContext =>
|
|
{
|
|
var accessToken = httpContext?.Features?.Get<IAuthenticateResultFeature>()?
|
|
.AuthenticateResult?.Properties?.GetTokenValue("access_token")?.ToString()
|
|
?? string.Empty;
|
|
if (!StringValues.IsNullOrEmpty(accessToken))
|
|
{
|
|
return RateLimitPartition.GetTokenBucketLimiter(accessToken, key =>
|
|
new TokenBucketRateLimiterOptions
|
|
{
|
|
TokenLimit = myOptions.tokenLimit2,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
ReplenishmentPeriod =
|
|
TimeSpan.FromSeconds(myOptions.replenishmentPeriod),
|
|
TokensPerPeriod = myOptions.tokensPerPeriod,
|
|
AutoReplenishment = myOptions.autoReplenishment
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return RateLimitPartition.GetTokenBucketLimiter("Anon", key =>
|
|
new TokenBucketRateLimiterOptions
|
|
{
|
|
TokenLimit = myOptions.tokenLimit,
|
|
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
|
|
QueueLimit = myOptions.queueLimit,
|
|
ReplenishmentPeriod =
|
|
TimeSpan.FromSeconds(myOptions.replenishmentPeriod),
|
|
TokensPerPeriod = myOptions.tokensPerPeriod,
|
|
AutoReplenishment = true
|
|
});
|
|
}
|
|
});
|
|
|
|
app.UseRateLimiter(options);
|
|
|
|
app.MapGet("/", () => "Hello, World!");
|
|
|
|
app.MapGet("/jwt", (HttpContext context) => $"Hello {GetUserEndPointMethod(context)}")
|
|
.RequireRateLimiting(jwtPolicyName)
|
|
.RequireAuthorization();
|
|
|
|
app.MapPost("/post", (HttpContext context) => $"Hello {GetUserEndPointMethod(context)}")
|
|
.RequireRateLimiting(jwtPolicyName)
|
|
.RequireAuthorization();
|
|
|
|
app.Run();
|
|
|
|
static string GetUserEndPointMethod(HttpContext context) =>
|
|
$"Hello {context.User?.Identity?.Name ?? "Anonymous"} " +
|
|
$"Endpoint:{context.Request.Path} Method: {context.Request.Method}";
|
|
|
|
// </snippet_jwt>
|
|
#endif
|