minimal api 踢用户下线 实现
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
947d6dc6d1
commit
74a0c8923c
|
@ -149,6 +149,7 @@ builder.Services.AddMasaMinimalAPIs(options =>
|
|||
|
||||
options.RouteHandlerBuilder= t=> {
|
||||
t.RequireAuthorization()
|
||||
.AddEndpointFilter<LimitUserRequestAuthorizationEndpointFilter>()
|
||||
.AddEndpointFilter<UnifiedApiResultEndpointFilter>()
|
||||
.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<IExceptionHandlerPathFeature>();
|
||||
//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<MultiDiskStaticFilesMiddleware>();
|
||||
|
|
|
@ -13,24 +13,10 @@ namespace IRaCIS.Core.Application.Filter;
|
|||
|
||||
|
||||
|
||||
public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
||||
public class LimitUserRequestAuthorization(
|
||||
IFusionCache _fusionCache, IUserInfo _userInfo, IStringLocalizer _localizer,
|
||||
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig) : IAsyncAuthorizationFilter
|
||||
{
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
private readonly IFusionCache _fusionCache;
|
||||
|
||||
private readonly IUserInfo _userInfo;
|
||||
|
||||
private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig;
|
||||
|
||||
public LimitUserRequestAuthorization(IFusionCache fusionCache, IUserInfo userInfo, IStringLocalizer localizer, IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig)
|
||||
{
|
||||
_fusionCache = fusionCache;
|
||||
_userInfo = userInfo;
|
||||
_verifyConfig = verifyConfig;
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
|
@ -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<ServiceVerifyConfigOption> _verifyConfig) : IEndpointFilter
|
||||
{
|
||||
|
||||
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||
{
|
||||
var minutes = _verifyConfig.CurrentValue.AutoLoginOutMinutes;
|
||||
|
||||
if (_verifyConfig.CurrentValue.OpenLoginLimit)
|
||||
{
|
||||
// 如果此请求允许匿名访问,不进行处理
|
||||
var endpoint = context.HttpContext.GetEndpoint();
|
||||
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != 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<string>(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<string>(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
|
|
@ -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<UnifiedApiResultEndpointFilter> _logger) : IEndpointFilter
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public UnifiedApiResultEndpointFilter(ILogger<UnifiedApiResultFilter> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async ValueTask<object?> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
public IResponseOutput GetTest()
|
||||
{
|
||||
throw new BusinessValidationFailedException("手动抛出的异常");
|
||||
//throw new BusinessValidationFailedException("手动抛出的异常");
|
||||
|
||||
return ResponseOutput.Ok(_userInfo.IP);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue