| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- namespace Admin.NET.Core;
- /// <summary>
- /// 当前用户 PC 菜单访问判定。
- /// </summary>
- public interface ICurrentUserMenuAccessService
- {
- /// <summary>
- /// 判断当前 JWT 用户是否拥有指定 PC 菜单 path。
- /// </summary>
- Task<bool> HasMenuPathAsync(string menuPath);
- }
- /// <summary>
- /// 要求当前用户拥有指定 PC 菜单 path。
- /// </summary>
- /// <remarks>
- /// 认证缺失返回 401;权限服务缺失、判定异常或无权限均返回 403(fail-closed)。
- /// 超级管理员和系统管理员的豁免口径由 <see cref="ICurrentUserMenuAccessService"/>
- /// 按登录菜单树规则统一处理。
- /// </remarks>
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
- public sealed class RequirePcMenuPathAttribute : Attribute, IAsyncAuthorizationFilter
- {
- /// <summary>
- /// 要求具备其中任意一个的 PC 菜单 path。
- /// </summary>
- public IReadOnlyList<string> MenuPaths { get; }
- /// <summary>兼容单 path 调用方;多 path 时返回首项。</summary>
- public string MenuPath => MenuPaths[0];
- public RequirePcMenuPathAttribute(params string[] menuPaths)
- {
- if (menuPaths == null || menuPaths.Length == 0 || menuPaths.Any(string.IsNullOrWhiteSpace))
- throw new ArgumentException("PC 菜单 path 不能为空", nameof(menuPaths));
- MenuPaths = menuPaths;
- }
- public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
- {
- if (context.HttpContext.User?.Identity?.IsAuthenticated != true)
- {
- context.Result = new UnauthorizedResult();
- return;
- }
- var accessService = context.HttpContext.RequestServices.GetService<ICurrentUserMenuAccessService>();
- if (accessService == null)
- {
- context.Result = Forbid();
- return;
- }
- try
- {
- foreach (var menuPath in MenuPaths)
- {
- if (await accessService.HasMenuPathAsync(menuPath)) return;
- }
- }
- catch
- {
- // 权限基础设施异常时不得放行受保护端点。
- }
- context.Result = Forbid();
- }
- private static ObjectResult Forbid() => new(new { message = "没有权限访问该功能" })
- {
- StatusCode = StatusCodes.Status403Forbidden
- };
- }
|