diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index daddbfb55..8aad500c0 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -149,6 +149,7 @@ builder.Services.AddMasaMinimalAPIs(options => options.RouteHandlerBuilder= t=> { t.RequireAuthorization() + .AddEndpointFilter() .AddEndpointFilter() .WithGroupName("Institution"); }; @@ -191,27 +192,6 @@ app.UseStatusCodePages(async context => //app.UseExceptionHandler(); app.UseExceptionHandler(o => { }); -//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug -//app.UseExceptionHandler(configure => -//{ -// configure.Run(async context => -// { -// var exceptionHandlerPathFeature = context.Features.Get(); -//var ex = exceptionHandlerPathFeature?.Error; -//context.Response.ContentType = "application/json"; - -//if (ex != null) -//{ -// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : ""); - -// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}"))); - -// Log.Logger.Error(errorInfo); - -//} -// }); -//}); - #region 暂时废弃 //app.UseMiddleware(); diff --git a/IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/EncreptApiResultFilter.cs similarity index 100% rename from IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs rename to IRaCIS.Core.Application/BusinessFilter/LegacyController/EncreptApiResultFilter.cs diff --git a/IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/LimitUserRequestAuthorization.cs similarity index 86% rename from IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs rename to IRaCIS.Core.Application/BusinessFilter/LegacyController/LimitUserRequestAuthorization.cs index ec5ea0863..69bafe0f9 100644 --- a/IRaCIS.Core.Application/BusinessFilter/LimitUserRequestAuthorization.cs +++ b/IRaCIS.Core.Application/BusinessFilter/LegacyController/LimitUserRequestAuthorization.cs @@ -13,24 +13,10 @@ namespace IRaCIS.Core.Application.Filter; -public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter +public class LimitUserRequestAuthorization( + IFusionCache _fusionCache, IUserInfo _userInfo, IStringLocalizer _localizer, + IOptionsMonitor _verifyConfig) : IAsyncAuthorizationFilter { - public IStringLocalizer _localizer { get; set; } - - private readonly IFusionCache _fusionCache; - - private readonly IUserInfo _userInfo; - - private readonly IOptionsMonitor _verifyConfig; - - public LimitUserRequestAuthorization(IFusionCache fusionCache, IUserInfo userInfo, IStringLocalizer localizer, IOptionsMonitor verifyConfig) - { - _fusionCache = fusionCache; - _userInfo = userInfo; - _verifyConfig = verifyConfig; - _localizer = localizer; - } - public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs similarity index 100% rename from IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs rename to IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelActionFilter .cs diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelBinding.cs similarity index 100% rename from IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs rename to IRaCIS.Core.Application/BusinessFilter/LegacyController/ModelBinding.cs diff --git a/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs b/IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs similarity index 100% rename from IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs rename to IRaCIS.Core.Application/BusinessFilter/LegacyController/TrialResourceFilter.cs diff --git a/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/LimitUserRequestAuthorizationEndpointFilter.cs b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/LimitUserRequestAuthorizationEndpointFilter.cs new file mode 100644 index 000000000..0d7a9f5a3 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/LimitUserRequestAuthorizationEndpointFilter.cs @@ -0,0 +1,85 @@ +using IRaCIS.Core.Application.Helper; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.BusinessFilter; + +#region minimalapi 流程 + + +public class LimitUserRequestAuthorizationEndpointFilter( + IFusionCache _fusionCache, IUserInfo _userInfo, IStringLocalizer _localizer, + IOptionsMonitor _verifyConfig) : IEndpointFilter +{ + + public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) + { + var minutes = _verifyConfig.CurrentValue.AutoLoginOutMinutes; + + if (_verifyConfig.CurrentValue.OpenLoginLimit) + { + // 如果此请求允许匿名访问,不进行处理 + var endpoint = context.HttpContext.GetEndpoint(); + if (endpoint?.Metadata?.GetMetadata() != null) + { + return await next(context); + } + + // 没有从请求中取到 token + if (string.IsNullOrWhiteSpace(_userInfo.UserToken)) + { + context.HttpContext.Response.ContentType = "application/json"; + return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"]), statusCode: StatusCodes.Status200OK); + } + + // 获取缓存中的用户 token + var cacheUserToken = await _fusionCache.GetOrDefaultAsync(CacheKeys.UserToken(_userInfo.Id)); + + // 缓存中没有取到 token + if (string.IsNullOrWhiteSpace(cacheUserToken)) + { + // 设置当前用户最新 token + await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _userInfo.UserToken, TimeSpan.FromDays(7)); + await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes)); + } + // 如果是同一个用户 + else if (cacheUserToken == _userInfo.UserToken) + { + var cacheTime = await _fusionCache.GetOrDefaultAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id)); + + // 如果过期,自动登出 + if (string.IsNullOrEmpty(cacheTime)) + { + context.HttpContext.Response.ContentType = "application/json"; + return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AccountAuto_LoginOut"], ApiResponseCodeEnum.AutoLoginOut), statusCode: StatusCodes.Status403Forbidden); + } + else + { + await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes)); + } + } + else + { + // 如果账户在其他地方已登录,当前用户被迫下线 + context.HttpContext.Response.ContentType = "application/json"; + return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AccountLoggedInElsewhere"], ApiResponseCodeEnum.LoginInOtherPlace), statusCode: StatusCodes.Status403Forbidden); + } + } + + // 如果通过授权,继续执行请求管道 + return await next(context); + + } + + +} + +#endregion \ No newline at end of file diff --git a/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/UnifiedApiResultEndpointFilter.cs b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/UnifiedApiResultEndpointFilter.cs index 559c9f3ab..008655bc5 100644 --- a/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/UnifiedApiResultEndpointFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/MinimalAPI/UnifiedApiResultEndpointFilter.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -11,14 +12,8 @@ namespace IRaCIS.Core.Application.Service.BusinessFilter; #region minimalapi 流程 -public class UnifiedApiResultEndpointFilter : IEndpointFilter +public class UnifiedApiResultEndpointFilter(ILogger _logger) : IEndpointFilter { - private readonly ILogger _logger; - - public UnifiedApiResultEndpointFilter(ILogger logger) - { - _logger = logger; - } public async ValueTask InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next) { @@ -37,8 +32,13 @@ public class UnifiedApiResultEndpointFilter : IEndpointFilter return ResponseOutput.Ok(tuple.Item1, tuple.Item2); } - if (result is IResponseOutput) - { + if (result is IResponseOutput responseOutput) + { + if (!string.IsNullOrWhiteSpace(responseOutput.LocalizedInfo)) + { + //统一在这里记录国际化的日志信息 + _logger.LogWarning($"{responseOutput.LocalizedInfo}"); + } return result; } diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index 727dfb5b5..b33c86a67 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -58,7 +58,7 @@ namespace IRaCIS.Core.Application.Service public IResponseOutput GetTest() { - throw new BusinessValidationFailedException("手动抛出的异常"); + //throw new BusinessValidationFailedException("手动抛出的异常"); return ResponseOutput.Ok(_userInfo.IP); }