docs: add xml comments and update ignore rules

This commit is contained in:
2025-12-12 10:39:51 +08:00
parent d38127d6b2
commit 715cbb3d36
24 changed files with 865 additions and 95 deletions

View File

@@ -1,34 +1,40 @@
using Microsoft.AspNetCore.Authorization;
using TakeoutSaaS.Module.Authorization.Policies;
namespace TakeoutSaaS.Module.Authorization.Attributes;
/// <summary>
/// 权限校验特性
/// 权限校验特性
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class PermissionAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// 初始化权限校验特性并构建对应策略。
/// </summary>
/// <param name="permissions">所需的权限标识集合。</param>
public PermissionAuthorizeAttribute(params string[] permissions)
{
// 1. 校验权限参数不为空
ArgumentNullException.ThrowIfNull(permissions);
// 2. 规范化权限标识
var normalized = permissions
.Where(p => !string.IsNullOrWhiteSpace(p))
.Select(p => p.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
// 3. 确保至少提供一个有效权限
if (normalized.Length == 0)
{
throw new ArgumentException("至少需要一个权限标识", nameof(permissions));
}
// 4. 绑定权限集合并生成策略名称
Permissions = normalized;
Policy = PermissionAuthorizationPolicyProvider.BuildPolicyName(normalized);
}
/// <summary>
/// 所需权限集合
/// 所需权限集合
/// </summary>
public IReadOnlyCollection<string> Permissions { get; }
}

View File

@@ -1,38 +1,45 @@
using Microsoft.AspNetCore.Authorization;
namespace TakeoutSaaS.Module.Authorization.Policies;
/// <summary>
/// 权限校验处理器
/// 权限校验处理器
/// </summary>
public sealed class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
/// <summary>
/// 用户声明中权限的声明类型键。
/// </summary>
public const string PermissionClaimType = "permission";
/// <summary>
/// 校验当前用户是否具备要求的权限集合。
/// </summary>
/// <param name="context">授权上下文。</param>
/// <param name="requirement">权限需求描述。</param>
/// <returns>异步完成任务。</returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
// 1. 校验用户已通过认证
if (context.User?.Identity?.IsAuthenticated != true)
{
return Task.CompletedTask;
}
// 2. 收集用户已授予的权限标识
var userPermissions = context.User
.FindAll(PermissionClaimType)
.Select(claim => claim.Value)
.Where(value => !string.IsNullOrWhiteSpace(value))
.Select(value => value.Trim())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
// 3. 无权限直接结束
if (userPermissions.Count == 0)
{
return Task.CompletedTask;
}
// 4. 任一权限匹配即视为授权通过
if (requirement.Permissions.Any(userPermissions.Contains))
{
context.Succeed(requirement);
}
// 5. 结束处理
return Task.CompletedTask;
}
}

View File

@@ -1,55 +1,62 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
namespace TakeoutSaaS.Module.Authorization.Policies;
/// <summary>
/// 权限策略提供者(按需动态构建策略)
/// 权限策略提供者(按需动态构建策略)
/// </summary>
public sealed class PermissionAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options) : DefaultAuthorizationPolicyProvider(options)
{
/// <summary>
/// 权限策略名称前缀。
/// </summary>
public const string PolicyPrefix = "PERMISSION:";
private readonly AuthorizationOptions _options = options.Value;
/// <summary>
/// 获取或构建指定名称的权限策略。
/// </summary>
/// <param name="policyName">策略名称。</param>
/// <returns>匹配的授权策略。</returns>
public override Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
{
if (policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
// 1. 非权限策略走基类逻辑
if (!policyName.StartsWith(PolicyPrefix, StringComparison.OrdinalIgnoreCase))
{
var existingPolicy = _options.GetPolicy(policyName);
if (existingPolicy != null)
{
return Task.FromResult<AuthorizationPolicy?>(existingPolicy);
}
var permissions = ParsePermissions(policyName);
if (permissions.Length == 0)
{
return Task.FromResult<AuthorizationPolicy?>(null);
}
var policy = new AuthorizationPolicyBuilder()
.AddRequirements(new PermissionRequirement(permissions))
.Build();
_options.AddPolicy(policyName, policy);
return Task.FromResult<AuthorizationPolicy?>(policy);
return base.GetPolicyAsync(policyName);
}
return base.GetPolicyAsync(policyName);
// 2. 复用已存在的策略
var existingPolicy = _options.GetPolicy(policyName);
if (existingPolicy != null)
{
return Task.FromResult<AuthorizationPolicy?>(existingPolicy);
}
// 3. 解析策略携带的权限列表
var permissions = ParsePermissions(policyName);
if (permissions.Length == 0)
{
return Task.FromResult<AuthorizationPolicy?>(null);
}
// 4. 动态构建策略并缓存
var policy = new AuthorizationPolicyBuilder()
.AddRequirements(new PermissionRequirement(permissions))
.Build();
_options.AddPolicy(policyName, policy);
// 5. 返回构建好的策略
return Task.FromResult<AuthorizationPolicy?>(policy);
}
/// <summary>
/// 根据权限集合构建策略名称
/// 根据权限集合构建策略名称
/// </summary>
/// <param name="permissions">权限标识集合。</param>
/// <returns>策略名称。</returns>
public static string BuildPolicyName(IEnumerable<string> permissions)
=> $"{PolicyPrefix}{string.Join('|', NormalizePermissions(permissions))}";
private static string[] ParsePermissions(string policyName)
{
// 1. 拆分策略名称得到原始权限列表
var raw = policyName[PolicyPrefix.Length..];
// 2. 规范化并过滤权限
return NormalizePermissions(raw.Split('|', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
}
private static string[] NormalizePermissions(IEnumerable<string> permissions)
=> [.. permissions
.Where(p => !string.IsNullOrWhiteSpace(p))