网站首页 网站源码
website
站点相关全部源代码,隐藏了一些关于服务器的信息
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Dpz.Core.Infrastructure.RateLimiting;

/// <summary>
/// 基于行为的限流中间件
/// 只检查IP是否因可疑行为被限流,不限制正常请求频率
/// </summary>
public class RateLimitMiddleware(
    RequestDelegate next,
    IIpRateLimitService rateLimitService,
    ILogger<RateLimitMiddleware> logger,
    RateLimitConfig? config = null
)
{
    private readonly RateLimitConfig _config = config ?? new RateLimitConfig();

    public async Task InvokeAsync(HttpContext context)
    {
        var clientIp = context.Request.GetIpAddress();
        if (string.IsNullOrEmpty(clientIp))
        {
            await next(context);
            return;
        }

        // 检查IP是否被限流(不记录任何访问信息)
        var result = await rateLimitService.CheckLimitStatusAsync(clientIp, context.RequestAborted);

        if (!result.IsAllowed)
        {
            // IP因可疑行为被限流,延迟后返回429状态码
            await HandleBlockedRequest(context, result.Message, result.DelayMs);
            return;
        }

        // IP未被限流,继续处理请求
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "处理请求时发生错误,IP: {ClientIp}", clientIp);
            throw;
        }
    }

    /// <summary>
    /// 处理被封禁的请求
    /// </summary>
    private async Task HandleBlockedRequest(HttpContext context, string message, int delayMs = 0)
    {
        var clientIp = context.Request.GetIpAddress();

        // 如果有延迟设置,先让攻击者等待
        if (delayMs > 0)
        {
            // 延迟前检查响应状态
            if (context.Response.HasStarted)
            {
                logger.LogWarning(
                    "IP {ClientIp} 限流时发现响应已开始,跳过延迟,路径: {RequestPath}",
                    clientIp,
                    context.Request.Path
                );
                return;
            }

            // 根据配置决定是否记录详细日志
            if (_config.EnableVerboseLogging)
            {
                logger.LogInformation(
                    "IP {ClientIp} 被限流,延迟 {DelayMs}ms 后返回429,路径: {RequestPath}",
                    clientIp,
                    delayMs,
                    context.Request.Path
                );
            }

            try
            {
                await Task.Delay(delayMs, context.RequestAborted);
            }
            catch (OperationCanceledException)
            {
                // 根据配置决定是否记录详细日志
                if (_config.EnableVerboseLogging)
                {
                    logger.LogInformation(
                        "IP {ClientIp} 在限流延迟期间取消请求,延迟时间: {DelayMs}ms",
                        clientIp,
                        delayMs
                    );
                }
                return; // 请求被取消,直接返回,不抛出异常
            }
        }

        // 检查响应是否已经开始,避免 "response has already started" 错误
        if (context.Response.HasStarted)
        {
            logger.LogWarning(
                "IP {ClientIp} 限流响应时发现响应已开始,无法设置429状态码,路径: {RequestPath}",
                clientIp,
                context.Request.Path
            );
            return;
        }

        // 延迟后返回429响应
        context.Response.StatusCode = 429; // Too Many Requests
        context.Response.ContentType = "application/json; charset=utf-8";

        var response = new
        {
            error = "IP_BLOCKED",
            message,
            timestamp = DateTime.UtcNow,
            path = context.Request.Path.Value,
            delayMs,
        };

        var jsonResponse = JsonSerializer.Serialize(
            response,
            new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }
        );

        await context.Response.WriteAsync(jsonResponse);

        logger.LogWarning(
            "IP {ClientIp} 被限流并延迟响应,路径: {RequestPath}, 原因: {Message}, 延迟: {DelayMs}ms",
            clientIp,
            context.Request.Path,
            message,
            delayMs
        );
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码实现了一个基于行为的限流中间件,主要用于在 ASP.NET Core 应用程序中限制可疑 IP 地址的请求频率。以下是代码的主要功能和工作原理的详细解释:

1. 中间件的定义

  • RateLimitMiddleware 是一个中间件类,构造函数接受四个参数:
    • RequestDelegate next:下一个中间件的委托,用于将请求传递给管道中的下一个组件。
    • IIpRateLimitService rateLimitService:一个服务接口,用于检查 IP 地址的限流状态。
    • ILogger<RateLimitMiddleware> logger:用于记录日志的日志记录器。
    • RateLimitConfig? config:可选的配置对象,用于设置限流的相关参数。

2. 处理请求

  • InvokeAsync 方法是中间件的核心逻辑:
    • 首先获取请求的客户端 IP 地址。
    • 如果 IP 地址为空,直接调用下一个中间件。
    • 使用 rateLimitService 检查该 IP 地址的限流状态。
    • 如果 IP 地址被限流(result.IsAllowedfalse),则调用 HandleBlockedRequest 方法处理被封禁的请求。
    • 如果 IP 地址未被限流,则继续处理请求,并在处理过程中捕获任何异常并记录错误日志。

3. 处理被封禁的请求

  • HandleBlockedRequest 方法用于处理被限流的请求:
    • 如果配置了延迟(delayMs 大于 0),则在返回响应之前让请求延迟一段时间。
    • 在延迟期间,如果请求被取消(例如,客户端断开连接),则记录日志并返回。
    • 如果响应已经开始,则记录警告并返回,避免抛出错误。
    • 如果没有问题,设置响应状态码为 429(Too Many Requests),并返回一个 JSON 格式的响应,包含错误信息、时间戳、请求路径和延迟时间。

4. 日志记录

  • 中间件在多个地方记录日志,包括:
    • 当 IP 地址被限流时,记录详细信息(如果启用了详细日志)。
    • 当请求在限流延迟期间被取消时,记录信息。
    • 当响应已经开始时,记录警告信息。

5. 配置选项

  • RateLimitConfig 类用于配置限流的相关参数,例如是否启用详细日志记录。

总结

这个中间件的主要目的是通过检查 IP 地址的行为来限制可疑请求,防止恶意攻击或滥用。它允许正常请求通过,同时对可疑的 IP 地址实施延迟和限制,确保应用程序的安全性和稳定性。

loading