解决.net接口防暴力调用问题

在 .NET 中,为解决接口防暴力调用问题,可通过限制请求频率实现。下面给出几种不同实现方式。

基于内存的简单速率限制

此方法适用于单服务器环境,它借助内存字典来记录每个客户端的请求次数和时间。

MemoryRateLimitMiddleware.cs

复制代码
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

public class MemoryRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ConcurrentDictionary<string, (int Count, DateTime LastRequestTime)> _requestCounts = new ConcurrentDictionary<string, (int Count, DateTime LastRequestTime)>();
    private readonly int _maxRequests;
    private readonly TimeSpan _timeWindow;

    public MemoryRateLimitMiddleware(RequestDelegate next, int maxRequests, TimeSpan timeWindow)
    {
        _next = next;
        _maxRequests = maxRequests;
        _timeWindow = timeWindow;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var clientIp = context.Connection.RemoteIpAddress.ToString();
        var now = DateTime.UtcNow;

        if (_requestCounts.TryGetValue(clientIp, out var entry))
        {
            if (now - entry.LastRequestTime < _timeWindow)
            {
                if (entry.Count >= _maxRequests)
                {
                    context.Response.StatusCode = 429;
                    await context.Response.WriteAsync("请求频率过高,请稍后再试。");
                    return;
                }
                _requestCounts[clientIp] = (entry.Count + 1, entry.LastRequestTime);
            }
            else
            {
                _requestCounts[clientIp] = (1, now);
            }
        }
        else
        {
            _requestCounts.TryAdd(clientIp, (1, now));
        }

        await _next(context);
    }
}

Startup.cs

复制代码
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseMiddleware<MemoryRateLimitMiddleware>(100, TimeSpan.FromMinutes(1));

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

基于 Redis 的分布式速率限制

若应用部署在分布式环境,可使用 Redis 存储请求信息。需安装 StackExchange.Redis 包。

RedisRateLimitMiddleware.cs

复制代码
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using StackExchange.Redis;

public class RedisRateLimitMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IDatabase _redisDb;
    private readonly int _maxRequests;
    private readonly TimeSpan _timeWindow;

    public RedisRateLimitMiddleware(RequestDelegate next, ConnectionMultiplexer redis, int maxRequests, TimeSpan timeWindow)
    {
        _next = next;
        _redisDb = redis.GetDatabase();
        _maxRequests = maxRequests;
        _timeWindow = timeWindow;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var clientIp = context.Connection.RemoteIpAddress.ToString();
        var key = $"request_count:{clientIp}";

        var count = await _redisDb.StringGetAsync(key);
        if (count.HasValue)
        {
            var currentCount = int.Parse(count);
            if (currentCount >= _maxRequests)
            {
                context.Response.StatusCode = 429;
                await context.Response.WriteAsync("请求频率过高,请稍后再试。");
                return;
            }
            await _redisDb.StringIncrementAsync(key);
        }
        else
        {
            await _redisDb.StringSetAsync(key, 1, _timeWindow);
        }

        await _next(context);
    }
}

StartupRedis.cs

复制代码
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;

public class StartupRedis
{
    public void ConfigureServices(IServiceCollection services)
    {
        var redis = ConnectionMultiplexer.Connect("localhost");
        services.AddSingleton<IConnectionMultiplexer>(redis);
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IConnectionMultiplexer redis)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseMiddleware<RedisRateLimitMiddleware>(redis, 100, TimeSpan.FromMinutes(1));

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

解释

  • 基于内存的简单速率限制 :借助 ConcurrentDictionary 记录每个客户端的请求次数和时间,当请求频率超出限制时,返回 429 状态码。
  • 基于 Redis 的分布式速率限制:使用 Redis 存储请求信息,能处理分布式环境下的请求频率限制。
相关推荐
码农刚子6 小时前
用 .NET 8 + uni-app 做一套多租户畜牧 SaaS:我们在秦巴牧云踩过的 6 个坑
uni-app·.net·saas
宝桥南山2 天前
Blazor Web Assembly - 体验一下Authentication State从Server共享给Client
microsoft·微软·c#·asp.net·.net·.netcore
rockey6272 天前
C#脚本引擎之AScript与DynamicExpresso对比
c#·.net·script·动态脚本
qq_548612453 天前
.NET 平台报表工具汇总(.NET Framework /.NET6-8,中国式复杂报表、Web 嵌入、填报、导出打印)
前端·.net
公子小六3 天前
基于.NET的Windows窗体编程之WinForms音频控件
windows·microsoft·c#·.net·音视频·winforms
界面开发小八哥3 天前
界面控件DevExpress Blazor v26.1新版亮点 - Navigation(导航)
javascript·.net·界面控件·blazor·devexpress·ui开发
用户298698530143 天前
C# 实现 PowerPoint 多格式转换:图片、PDF 与 SVG 实战
c#·.net·svg
李高钢3 天前
【WPF】从 Avalon 到 .NET 10:WPF 平台演进史与特性全览
.net·wpf
刚子编程4 天前
用 Inno Setup 把 .NET 程序打包成安装包:从零到发布的完整指南
.net·inno setup·码农刚子·.net 程序打包·安装脚本编写
界面开发小八哥4 天前
界面控件DevExpress Blazor v26.1新版亮点 - TreeList & 数据编辑器
javascript·编辑器·.net·界面控件·blazor·devexpress·用户界面