ASP.NET Core授权:如何获取首个失败要求并终止检查?策略是否适用?
两种解决方案供你选择
方案1:为每个要求创建专属策略(推荐,更直观易维护)
如果你的核心需求是明确知晓哪项要求失败,同时可以灵活控制验证顺序和终止逻辑,为每个IAuthorizationRequirement单独创建策略是最清晰的方案。
注册策略示例
services.AddAuthorization(options => { options.AddPolicy("FooPolicy", policy => policy.AddRequirements(new FooRequirement())); options.AddPolicy("BarPolicy", policy => policy.AddRequirements(new BarRequirement())); options.AddPolicy("YetAnotherPolicy", policy => policy.AddRequirements(new YetAnotherRequirement())); });
手动按顺序验证(失败即终止)
// 先验证Foo要求 var fooAuthResult = await _authorizationService.AuthorizeAsync(User, resource, "FooPolicy"); if (!fooAuthResult.Succeeded) { // 记录FooRequirement失败,直接返回,终止后续检查 var failedFooReq = fooAuthResult.Failure?.FailedRequirements.FirstOrDefault(); // 这里可以处理失败逻辑,比如返回对应提示 return; } // 再验证Bar要求 var barAuthResult = await _authorizationService.AuthorizeAsync(User, resource, "BarPolicy"); if (!barAuthResult.Succeeded) { // 记录BarRequirement失败,终止后续 var failedBarReq = barAuthResult.Failure?.FailedRequirements.FirstOrDefault(); return; } // 最后验证YetAnother要求 var yetAnotherAuthResult = await _authorizationService.AuthorizeAsync(User, resource, "YetAnotherPolicy"); if (!yetAnotherAuthResult.Succeeded) { var failedYetAnotherReq = yetAnotherAuthResult.Failure?.FailedRequirements.FirstOrDefault(); return; } // 所有要求都通过,继续业务逻辑
这种方式的好处是:
- 每个策略职责单一,便于测试和维护
- 完全控制验证顺序,失败时能精准定位到具体是哪项要求没通过
- 可以灵活调整验证逻辑(比如某些要求是可选的,或者按不同顺序验证)
方案2:自定义顺序执行的授权处理器(保留单一策略)
如果你希望保留一个统一的SomePolicy,但需要实现“失败即终止”的逻辑,可以自定义一个IAuthorizationHandler,按顺序逐个验证要求,一旦失败就停止后续处理。
自定义顺序处理器示例
public class SequentialAuthorizationHandler : IAuthorizationHandler { private readonly IEnumerable<IAuthorizationHandler> _allHandlers; // 注入系统中所有已注册的授权处理器 public SequentialAuthorizationHandler(IEnumerable<IAuthorizationHandler> allHandlers) { _allHandlers = allHandlers; } public async Task HandleAsync(AuthorizationHandlerContext context) { // 按策略中添加的顺序处理每个未完成的要求 foreach (var requirement in context.PendingRequirements.ToList()) { // 找到对应要求的处理器 var handler = _allHandlers.FirstOrDefault(h => h.GetType().IsAssignableTo(typeof(IAuthorizationHandler<>).MakeGenericType(requirement.GetType()))); if (handler == null) { // 如果找不到对应处理器,标记失败并终止 context.Fail(new AuthorizationFailureReason(this, $"No handler found for {requirement.GetType().Name}")); break; } // 执行当前要求的验证 await handler.HandleAsync(context); // 如果当前要求未通过,终止后续所有验证 if (!context.HasSucceeded) { break; } } } }
注册自定义处理器
// 注册自定义顺序处理器 services.AddScoped<IAuthorizationHandler, SequentialAuthorizationHandler>(); // 仍然保留你的原策略注册 services.AddAuthorization(options => { options.AddPolicy("SomePolicy", policy => { policy.AddRequirements(new FooRequirement()); policy.AddRequirements(new BarRequirement()); policy.AddRequirements(new YetAnotherRequirement()); }); });
这种方式的好处是可以保留单一策略,但需要额外编写自定义处理器,复杂度比方案1高一些。
总结
- 现有默认策略机制无法满足你“失败即终止并定位具体失败项”的需求
- 推荐优先选择方案1(每个要求一个策略),代码更清晰、易维护,也更灵活
- 如果必须保留单一策略,可以用**方案2(自定义顺序处理器)**实现需求
内容的提问来源于stack exchange,提问作者Pierre Roudaut
相关产品推荐
相关产品推荐

