diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index ae03f93a3..69be6d74f 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -1,6 +1,7 @@ using IRaCIS.Core.API; using IRaCIS.Core.API.HostService; using IRaCIS.Core.Application.BusinessFilter; +using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.MassTransit.Consumer; using IRaCIS.Core.Application.Service; @@ -100,6 +101,7 @@ builder.Services.AddControllers(options => options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); + options.Filters.Add(); }) .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 diff --git a/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs index 7541ecf25..075a7fd9e 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/ServiceCollectionSetup.cs @@ -28,6 +28,7 @@ public static class ServiceCollectionSetup services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig")); + services.AddOptions().Configure(_configuration.GetSection("RequestDuplicationOptions")); services.AddOptions().Configure(_configuration.GetSection("SystemPacsConfig")); services.Configure(_configuration.GetSection("IRaCISBasicConfig")); diff --git a/IRaCIS.Core.API/appsettings.Event_IRC.json b/IRaCIS.Core.API/appsettings.Event_IRC.json index 392b55d49..6ec66b5b2 100644 --- a/IRaCIS.Core.API/appsettings.Event_IRC.json +++ b/IRaCIS.Core.API/appsettings.Event_IRC.json @@ -69,7 +69,13 @@ "CompanyShortName": "Extensive Imaging", "CompanyShortNameCN": "展影医疗", "IsEnv_US": false + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } - } diff --git a/IRaCIS.Core.API/appsettings.Prod_IRC.json b/IRaCIS.Core.API/appsettings.Prod_IRC.json index b0f6caeed..9985c978b 100644 --- a/IRaCIS.Core.API/appsettings.Prod_IRC.json +++ b/IRaCIS.Core.API/appsettings.Prod_IRC.json @@ -78,6 +78,12 @@ "SystemPacsConfig": { "Port": "11113", "IP": "101.132.193.237" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } - } diff --git a/IRaCIS.Core.API/appsettings.Test_IRC.json b/IRaCIS.Core.API/appsettings.Test_IRC.json index f266ee6b3..a86722863 100644 --- a/IRaCIS.Core.API/appsettings.Test_IRC.json +++ b/IRaCIS.Core.API/appsettings.Test_IRC.json @@ -160,5 +160,16 @@ "Port": "11113", // PACS服务器IP地址 "IP": "106.14.89.110" + }, + // 重复请求配置 + "RequestDuplicationOptions": { + // 是否启用重复请求检测 + "IsEnabled": true, + // 重复请求时间窗口(毫秒) + "DuplicationWindowMs": 500, + // 缓存请求时间(秒) + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } diff --git a/IRaCIS.Core.API/appsettings.Test_IRC_PGSQL.json b/IRaCIS.Core.API/appsettings.Test_IRC_PGSQL.json index 4d17342dc..02bcd1cb8 100644 --- a/IRaCIS.Core.API/appsettings.Test_IRC_PGSQL.json +++ b/IRaCIS.Core.API/appsettings.Test_IRC_PGSQL.json @@ -91,5 +91,12 @@ "SystemPacsConfig": { "Port": "11113", "IP": "106.14.89.110" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } diff --git a/IRaCIS.Core.API/appsettings.US_Prod_IRC.json b/IRaCIS.Core.API/appsettings.US_Prod_IRC.json index 26935cc91..286366cfc 100644 --- a/IRaCIS.Core.API/appsettings.US_Prod_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Prod_IRC.json @@ -85,6 +85,13 @@ "SystemPacsConfig": { "Port": "104", "IP": "44.210.231.169" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } diff --git a/IRaCIS.Core.API/appsettings.US_Test_IRC.json b/IRaCIS.Core.API/appsettings.US_Test_IRC.json index 043191045..df94467fb 100644 --- a/IRaCIS.Core.API/appsettings.US_Test_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Test_IRC.json @@ -92,6 +92,13 @@ "SystemPacsConfig": { "Port": "104", "IP": "3.226.182.187" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } diff --git a/IRaCIS.Core.API/appsettings.US_Uat_IRC.json b/IRaCIS.Core.API/appsettings.US_Uat_IRC.json index e303cc381..29856f89e 100644 --- a/IRaCIS.Core.API/appsettings.US_Uat_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Uat_IRC.json @@ -92,6 +92,12 @@ "SystemPacsConfig": { "Port": "104", "IP": "3.226.182.187" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } - } diff --git a/IRaCIS.Core.API/appsettings.Uat_IRC.json b/IRaCIS.Core.API/appsettings.Uat_IRC.json index c617a54d4..05eaba3c2 100644 --- a/IRaCIS.Core.API/appsettings.Uat_IRC.json +++ b/IRaCIS.Core.API/appsettings.Uat_IRC.json @@ -99,6 +99,13 @@ "SystemPacsConfig": { "Port": "11113", "IP": "101.132.253.119" + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } diff --git a/IRaCIS.Core.API/appsettings.json b/IRaCIS.Core.API/appsettings.json index 4568c3524..66c347795 100644 --- a/IRaCIS.Core.API/appsettings.json +++ b/IRaCIS.Core.API/appsettings.json @@ -74,5 +74,12 @@ "redirect_uri": "https://oauthlogin.net/oauth/githubcallback", "scope": "repo" } + }, + "RequestDuplicationOptions": { + "IsEnabled": true, + "DuplicationWindowMs": 500, + "CacheTimeSeconds": 5, + "ExcludedPaths": [ + ] } } \ No newline at end of file diff --git a/IRaCIS.Core.Application/BusinessFilter/LegacyController/RequestDuplicationFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/RequestDuplicationFilter.cs new file mode 100644 index 000000000..5925332c3 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/RequestDuplicationFilter.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.BusinessFilter.LegacyController +{ + using Database.Api; + using DocumentFormat.OpenXml.InkML; + using IRaCIS.Core.Application.Service.Common; + using IRaCIS.Core.Infrastructure; + using Microsoft.AspNetCore.Components.Endpoints; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Filters; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + using Minio.Helper; + using Newtonsoft.Json; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography; + using System.Threading.Tasks; + + namespace Database.Api + { + /// + /// 请求拦截 请求前后的操作 + /// + /// logger + /// loggerHelper + public class RequestDuplicationFilter(ILogger logger, IHttpContextAccessor accessor, IUserInfo _userInfo, IStringLocalizer _localizer, IOptionsMonitor RequestDuplicationOptionsMonitor) : ExceptionFilterAttribute, IAsyncActionFilter + { + /// + /// 传入的参数 + /// + public string Intoparam { get; set; } + + /// + /// 这个是正常记录(请求刚进入的时候) + /// + /// context + /// next + /// 返回的对象 + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + this.Intoparam = JsonConvert.SerializeObject(context.ActionArguments); + try + { + this.RequestDuplication(); + } + catch (Exception) + { + throw; + } + + + var resultContext = await next(); + } + + + + /// + /// + /// + private void RequestDuplication() + { + var requestPath = accessor?.HttpContext?.Request?.Path.ToString() ?? string.Empty; + + // 验证请求频繁情况 + if (_userInfo.UserRoleId != default(Guid)&& + RequestDuplicationOptionsMonitor.CurrentValue.IsEnabled && + !RequestDuplicationOptionsMonitor.CurrentValue.ExcludePaths.Contains(requestPath)) + { + RequestInfo requestInfo = new RequestInfo + { + UserRoleId = _userInfo.UserRoleId, + RequestPath = requestPath, + ParameterHash = GenerateParameterHash(this.Intoparam), + RequestTime = DateTime.Now + }; + + IRCSystemInfo.RequestRecordList= IRCSystemInfo.RequestRecordList.Where(x => x.RequestTime >= DateTime.Now.AddSeconds(-RequestDuplicationOptionsMonitor.CurrentValue.CacheTimeSeconds)).ToList(); + + + var requestsTimes = IRCSystemInfo.RequestRecordList.Any(x=> + x.RequestTime>= requestInfo.RequestTime.AddMilliseconds(-RequestDuplicationOptionsMonitor.CurrentValue.DuplicationWindowMs)&& + x.RequestKey== requestInfo.RequestKey); + if (requestsTimes) + { + throw new BusinessValidationFailedException(_localizer["RequestDuplicationFilter_RequestDuplication"]); + } + IRCSystemInfo.RequestRecordList.Add(requestInfo); + } + } + + public string GenerateParameterHash(string parameters) + { + if (string.IsNullOrEmpty(parameters)) + return string.Empty; + + using var sha256 = SHA256.Create(); + var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(parameters)); + return Convert.ToBase64String(hashBytes); + } + + + + } + } +} diff --git a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs index b725834a6..f4bd22935 100644 --- a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs +++ b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs @@ -105,6 +105,54 @@ public class IRCEncreptOption public List ApiPathList { get; set; } } +/// +/// 请求缓存配置 +/// +public class RequestDuplicationOptions +{ + /// + /// 缓存时间(秒),默认5秒 + /// + public int CacheTimeSeconds { get; set; } = 5; + + /// + /// 重复请求检测时间窗口(毫秒),默认500毫秒 + /// + public int DuplicationWindowMs { get; set; } = 500; + + /// + /// 是否启用防重复请求 + /// + public bool IsEnabled { get; set; } = true; + + /// + /// 需要排除的路径(不进行重复检测) + /// + public List ExcludePaths { get; set; } = new List(); +} + +public static class IRCSystemInfo +{ + public static List RequestRecordList { get; set; } = new List(); +} + +public class RequestInfo +{ + public Guid UserRoleId { get; set; } + + public string RequestPath { get; set; } = string.Empty; + + public string ParameterHash { get; set; } = string.Empty; + + public DateTime RequestTime { get; set; } + + + /// + /// 请求的唯一标识 + /// + public string RequestKey => $"{UserRoleId}_{RequestPath}_{ParameterHash}"; +} + public class IRaCISBasicConfigOption { public string DoctorCodePrefix { get; set; } diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 9017143c9..cc3c6c45b 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -3233,6 +3233,20 @@ + + + 计算靶病灶状态 + + + + + + + 计算靶病灶融合后状态 + + + + 阅片导入 @@ -13057,7 +13071,7 @@ - 新增修改想想项目表格问题 + 新增修改项目表格问题 @@ -13158,7 +13172,7 @@ - + IR影像阅片 @@ -14398,6 +14412,38 @@ 测试加密API 返回的结果 + + + 请求拦截 请求前后的操作 + + logger + loggerHelper + + + + 请求拦截 请求前后的操作 + + logger + loggerHelper + + + + 传入的参数 + + + + + 这个是正常记录(请求刚进入的时候) + + context + next + 返回的对象 + + + + + + minimal api 生效,但是传统控制器,没生效 @@ -18859,6 +18905,36 @@ + + + 请求缓存配置 + + + + + 缓存时间(秒),默认5秒 + + + + + 重复请求检测时间窗口(毫秒),默认500毫秒 + + + + + 是否启用防重复请求 + + + + + 需要排除的路径(不进行重复检测) + + + + + 请求的唯一标识 + + Id(阅片期Id 或者 访视ID)