C# 自定义授权要求方法 C#如何创建IAuthorizationRequirement和Handler

8次阅读

IAuthorizationRequirement是仅用于类型标记的空接口,要求不可变且通过构造函数传参;Handler必须继承AuthorizationHandler并重写HandleRequirementAsync,注册需为Scoped且策略名、类型、参数须完全匹配。

C# 自定义授权要求方法 C#如何创建IAuthorizationRequirement和Handler

IAuthorizationRequirement 是个空接口,只起标记作用

它本身不带任何逻辑,纯粹用于类型区分——比如你定义 MinimumAgeRequirementHasPermissionRequirement,都继承自它,目的是让 Handler 能通过泛型匹配到对应处理逻辑。不要试图在里面加属性或方法来“控制授权流程”,那是 Handler 的事。

常见错误:给 Requirement 加 public bool IsSatisfied { get; set; } 这类运行时状态字段。它会被反复实例化,状态无法保持,也违背了 Requirement 应该是不可变(immutable)设计原则。

正确做法是把必要参数全塞进构造函数,并设为只读:

public class MinimumAgeRequirement : IAuthorizationRequirement {     public int MinimumAge { get; }     public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge; }

Handler 必须继承 AuthorizationHandler 且重写 HandleRequirementAsync

这是真正干活的地方。ASP.net Core 在授权时会查找所有注册的 AuthorizationHandler,并把当前 AuthorizationHandlerContext 和匹配的 Requirement 实例传进来。你得在这个方法里决定调用 context.Succeed(requirement) 还是 context.Fail()

关键点:

  • 必须用 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) 签名,泛型参数 T 必须和你注册的 Requirement 类型一致
  • 不能漏掉 context.Fail() —— 如果条件不满足又没调用 Fail,授权会“静默通过”(因为默认不失败)
  • 不要在 Handler 里直接 throw 异常,授权失败走 Fail(),异常留给中间件层处理
  • 如果需要访问用户信息,从 context.User 拿;要读 Claim,别手动遍历,用 context.User.FindFirst("age")?.Valuecontext.User.HasClaim()

注册 Requirement 和 Handler 要配对,且顺序影响行为

Program.cs(.NET 6+)中注册时,先用 AddAuthorization 添加策略,再用 AddScoped 注册 Handler。Requirement 不需要单独注册,但策略里要用到它:

builder.Services.AddAuthorization(options => {     options.AddPolicy("MinimumAge18", policy =>         policy.Requirements.Add(new MinimumAgeRequirement(18))); });  builder.Services.AddScoped();

注意:

  • 多个 Handler 可以处理同一个 Requirement,它们都会被执行(除非某个调用了 context.Succeed() 后你主动 return
  • 如果一个策略绑了多个 Requirement,所有对应 Handler 都必须成功,策略才算通过
  • Handler 注册必须是 Scoped(不是 Singleton),因为 AuthorizationHandlerContext 是每次请求新建的

调试时最常卡在 Handler 没被触发

现象:加了断点但完全不进 HandleRequirementAsync,策略始终失败。大概率是下面几个原因:

  • Handler 类型没注册,或者注册成了 AddSingleton 导致依赖注入失败
  • 策略名拼错,比如控制器上写了 [Authorize(Policy = "MinAge18")],但注册的是 "MinimumAge18"
  • Requirement 构造时传参出错,比如 new MinimumAgeRequirement(NULL) 导致构造函数抛异常,Handler 根本没创建出来
  • .NET 版本差异:.NET 5+ 要求 Handler 必须实现 IAuthorizationHandler 接口(虽然继承 AuthorizationHandler 已隐式实现,但某些反射场景会校验)

建议加一行日志在 Handler 构造函数里,确认它是否被创建;再在 HandleRequirementAsync 开头打日志,看是否进入。比断点更可靠。

Requirement 和 Handler 的耦合其实很轻,但注册链上任意一环断开,整个授权就静默失效——这点最容易被忽略。

text=ZqhQzanResources