From e5cf289da1de37c8337da77449af42443c4b0a41 Mon Sep 17 00:00:00 2001 From: claw_bot Date: Wed, 11 Mar 2026 16:13:27 +0000 Subject: [PATCH] =?UTF-8?q?P0=20=E5=AE=9E=E7=8E=B0=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E9=99=90=E6=B5=81=E4=B8=AD=E9=97=B4=E4=BB=B6=EF=BC=9A=E6=AF=8F?= =?UTF-8?q?=E5=88=86=E9=92=9F60=E6=AC=A1=E8=AF=B7=E6=B1=82=E9=99=90?= =?UTF-8?q?=E5=88=B6=EF=BC=8C=E4=BF=9D=E6=8A=A4=E7=AC=AC=E4=B8=89=E6=96=B9?= =?UTF-8?q?API=E9=85=8D=E9=A2=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Middleware/RateLimitMiddleware.cs | 68 +++++++++++++++++++ AssetManager.API/Program.cs | 3 + 2 files changed, 71 insertions(+) create mode 100644 AssetManager.API/Middleware/RateLimitMiddleware.cs diff --git a/AssetManager.API/Middleware/RateLimitMiddleware.cs b/AssetManager.API/Middleware/RateLimitMiddleware.cs new file mode 100644 index 0000000..e33e7f2 --- /dev/null +++ b/AssetManager.API/Middleware/RateLimitMiddleware.cs @@ -0,0 +1,68 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; + +namespace AssetManager.API.Middleware; + +/// +/// 请求限流中间件:限制每个用户的请求频率,保护第三方API配额 +/// +public class RateLimitMiddleware +{ + private readonly RequestDelegate _next; + private readonly IMemoryCache _cache; + private readonly ILogger _logger; + + // 限流配置:每个用户每分钟最多请求60次(平均1秒1次,足够正常使用) + private const int Limit = 60; + private const int WindowSeconds = 60; + private const string CacheKeyPrefix = "RateLimit_"; + + public RateLimitMiddleware(RequestDelegate next, IMemoryCache cache, ILogger logger) + { + _next = next; + _cache = cache; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + // 获取用户ID(未登录则用IP地址) + var userId = context.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value + ?? context.Connection.RemoteIpAddress?.ToString() + ?? "anonymous"; + + var cacheKey = $"{CacheKeyPrefix}{userId}"; + + // 获取当前窗口的请求计数 + var requestCount = _cache.Get(cacheKey); + + if (requestCount >= Limit) + { + _logger.LogWarning("用户 {UserId} 请求超过限流限制,当前计数: {Count}", userId, requestCount); + context.Response.StatusCode = StatusCodes.Status429TooManyRequests; + context.Response.ContentType = "application/json"; + await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(new + { + code = 429, + message = "请求过于频繁,请稍后再试", + data = (object)null + })); + return; + } + + // 计数+1,首次访问设置过期时间 + if (requestCount == 0) + { + _cache.Set(cacheKey, 1, TimeSpan.FromSeconds(WindowSeconds)); + } + else + { + _cache.Set(cacheKey, requestCount + 1, _cache.GetEntry(cacheKey).AbsoluteExpirationRelativeToNow ?? TimeSpan.FromSeconds(WindowSeconds)); + } + + await _next(context); + } +} diff --git a/AssetManager.API/Program.cs b/AssetManager.API/Program.cs index b564026..946ac26 100644 --- a/AssetManager.API/Program.cs +++ b/AssetManager.API/Program.cs @@ -118,6 +118,9 @@ app.Services.InitializeDatabase(); // 全局异常处理中间件(必须放在最前面) app.UseMiddleware(); +// 请求限流中间件:限制用户请求频率,保护第三方API配额 +app.UseMiddleware(); + if (app.Environment.IsDevelopment()) { app.UseSwagger();