From 19c738b974e14b62929dc69c2c6ac1e8540aad98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=81=9A=E7=A0=81=E5=86=9C?= <599854767@qq.com> Date: Mon, 4 Apr 2022 18:53:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=A0IPRateLimit=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ZR.Admin.WebApi/Extensions/IPRateExtension.cs | 27 ++++++++ ZR.Admin.WebApi/Startup.cs | 5 ++ ZR.Admin.WebApi/ZR.Admin.WebApi.csproj | 1 + ZR.Admin.WebApi/appsettings.json | 69 +++++++++++++++++++ ZR.Vue/src/utils/request.js | 8 +-- 5 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 ZR.Admin.WebApi/Extensions/IPRateExtension.cs diff --git a/ZR.Admin.WebApi/Extensions/IPRateExtension.cs b/ZR.Admin.WebApi/Extensions/IPRateExtension.cs new file mode 100644 index 0000000..ea0da14 --- /dev/null +++ b/ZR.Admin.WebApi/Extensions/IPRateExtension.cs @@ -0,0 +1,27 @@ +using AspNetCoreRateLimit; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace ZR.Admin.WebApi.Extensions +{ + public static class IPRateExtension + { + public static void AddIPRate(this IServiceCollection services, IConfiguration configuration) + { + if (services == null) throw new ArgumentNullException(nameof(services)); + + //从appsettings.json中加载常规配置,IpRateLimiting与配置文件中节点对应 + services.Configure(configuration.GetSection("IpRateLimiting")); + + //从appsettings.json中加载Ip规则 + services.Configure(configuration.GetSection("IpRateLimitPolicies")); + //注入计数器和规则存储 + services.AddSingleton(); + services.AddSingleton(); + //配置(解析器、计数器密钥生成器) + services.AddSingleton(); + services.AddSingleton(); + } + } +} diff --git a/ZR.Admin.WebApi/Startup.cs b/ZR.Admin.WebApi/Startup.cs index c57fc79..e60e549 100644 --- a/ZR.Admin.WebApi/Startup.cs +++ b/ZR.Admin.WebApi/Startup.cs @@ -1,3 +1,4 @@ +using AspNetCoreRateLimit; using Hei.Captcha; using Infrastructure; using Infrastructure.Extensions; @@ -56,7 +57,9 @@ namespace ZR.Admin.WebApi .PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection")); //֤ͨ services.AddHeiCaptcha(); + services.AddIPRate(Configuration); services.AddSession(); + services.AddMemoryCache(); services.AddHttpContextAccessor(); //Model @@ -127,6 +130,8 @@ namespace ZR.Admin.WebApi app.UseAddTaskSchedulers(); //ʹȫ쳣м app.UseMiddleware(); + //ÿͻIP + app.UseIpRateLimiting(); app.UseEndpoints(endpoints => { diff --git a/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj index ba7b99e..63243c0 100644 --- a/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj +++ b/ZR.Admin.WebApi/ZR.Admin.WebApi.csproj @@ -22,6 +22,7 @@ + diff --git a/ZR.Admin.WebApi/appsettings.json b/ZR.Admin.WebApi/appsettings.json index f4ddf7b..99891ab 100644 --- a/ZR.Admin.WebApi/appsettings.json +++ b/ZR.Admin.WebApi/appsettings.json @@ -54,5 +54,74 @@ "RedisServer": { "Cache": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:", "Session": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:" + }, + //ӿ + "IpRateLimiting": { + //5ÿӷFalseʱĿÿӿڶĸӿڣֻҪһۼƹ5Σֹʡ + //Trueһ5GetDataӿڣýӿڽʱڽֹʣǻԷPostData()5,ܵ˵ÿӿڶ5һӣš + "EnableEndpointRateLimiting": true, + //falseܾAPIòӵô; ͻÿ뷢3ÿһõƣÿӻÿƽ¼һãɹAPIáϣܾAPIüʱʾӣСʱȣ + //StackBlockedRequestsΪtrue + "StackBlockedRequests": false, + "RealIpHeader": "X-Real-IP", + //ȡĿͻID˱ͷдڿͻIDClientWhitelistֵָƥ䣬Ӧơ + "ClientIdHeader": "X-ClientId", + "HttpStatusCode": 429, + //˵ + //"EndpointWhitelist": [ "get:/api/xxx", "*:/api/yyy" ], + //ͻ˰ + //"ClientWhitelist": [ "dev-id-1", "dev-id-2" ], + "QuotaExceededResponse": { + "Content": "{{\"code\":429,\"msg\":\"ʹƵԺ\"}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + //ͨùapi,βһҪ* + "GeneralRules": [ + { + "Endpoint": "*:/captchaImage", + //ʱΣʽ{}{λ}ʹõλs, m, h, d + "Period": "3s", + "Limit": 5 + }, + { + "Endpoint": "post:*", + //ʱΣʽ{}{λ}ʹõλs, m, h, d + "Period": "3s", + "Limit": 1 + }, + { + "Endpoint": "put:*", + //ʱΣʽ{}{λ}ʹõλs, m, h, d + "Period": "3s", + "Limit": 1 + } + //{ + // "Endpoint": "*", + // //ʱΣʽ{}{λ}ʹõλs, m, h, d + // "Period": "1s", + // "Limit": 2 + //} + //{ + // "Endpoint": "*", + // "Period": "15m", + // "Limit": 100 + //}, + //{ + // "Endpoint": "*", + // "Period": "12h", + // "Limit": 1000 + //}, + //{ + // "Endpoint": "*", + // "Period": "7d", + // "Limit": 10000 + //} + ], + "IpRateLimitPolicies": { + //ip + "IpRules": [ + ] + } } } diff --git a/ZR.Vue/src/utils/request.js b/ZR.Vue/src/utils/request.js index e67bdd3..0d18e3b 100644 --- a/ZR.Vue/src/utils/request.js +++ b/ZR.Vue/src/utils/request.js @@ -57,7 +57,7 @@ service.interceptors.response.use(res => { }) return Promise.reject('无效的会话,或者会话已过期,请重新登录。') - } else if (code == 0 || code == 1 || code == 110 || code == 101 || code == 403 || code == 500) { + } else if (code == 0 || code == 1 || code == 110 || code == 101 || code == 403 || code == 500 || code == 429) { Message({ message: msg, type: 'error' @@ -70,13 +70,13 @@ service.interceptors.response.use(res => { }, error => { console.log('err' + error) - let { - message - } = error; + let { message } = error; if (message == "Network Error") { message = "后端接口连接异常"; } else if (message.includes("timeout")) { message = "系统接口请求超时"; + } else if (message.includes("Request failed with status code 429")) { + message = "请求过于频繁,请稍后再试"; } else if (message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; }