.NET Core如何通过Attribute为Authorization Handler提供实例?
完全可以通过自定义[Authorize]属性结合DI容器来实现每个端点的个性化授权逻辑,以下是具体落地步骤:
1. 定义端点授权检查接口
先抽象出所有个性化授权逻辑需要实现的统一接口:
public interface IEndpointAuthorizationChecker { Task<bool> CheckAuthorizationAsync(AuthorizationHandlerContext context); }
2. 自定义Authorize属性
扩展原生AuthorizeAttribute,添加属性指定当前端点要使用的授权检查实现类:
public class AuthorizeWithCheckerAttribute : AuthorizeAttribute { public Type CheckerType { get; } public AuthorizeWithCheckerAttribute(Type checkerType) { if (!typeof(IEndpointAuthorizationChecker).IsAssignableFrom(checkerType)) { throw new ArgumentException("指定的检查器类型必须实现IEndpointAuthorizationChecker接口"); } CheckerType = checkerType; // 用检查器类型全名作为策略名称,确保每个检查器对应唯一策略 Policy = checkerType.FullName; } }
3. 自定义PolicyProvider
重写DefaultAuthorizationPolicyProvider,根据策略名称(检查器类型全名)生成对应授权策略:
public class CheckerBasedPolicyProvider : DefaultAuthorizationPolicyProvider { public CheckerBasedPolicyProvider(IOptions<AuthorizationOptions> options) : base(options) { } public override async Task<AuthorizationPolicy> GetPolicyAsync(string policyName) { // 优先获取原生策略 var policy = await base.GetPolicyAsync(policyName); if (policy != null) return policy; // 根据检查器类型创建自定义策略 var checkerType = Type.GetType(policyName); if (checkerType != null && typeof(IEndpointAuthorizationChecker).IsAssignableFrom(checkerType)) { var policyBuilder = new AuthorizationPolicyBuilder(); policyBuilder.AddRequirements(new CheckerAuthorizationRequirement(checkerType)); return policyBuilder.Build(); } return null; } } // 自定义授权要求,用于携带检查器类型 public class CheckerAuthorizationRequirement : IAuthorizationRequirement { public Type CheckerType { get; } public CheckerAuthorizationRequirement(Type checkerType) { CheckerType = checkerType; } }
4. 实现自定义AuthorizationHandler
在Handler中从DI容器获取对应检查器实例,执行授权检查:
public class CheckerAuthorizationHandler : AuthorizationHandler<CheckerAuthorizationRequirement> { private readonly IServiceProvider _serviceProvider; public CheckerAuthorizationHandler(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, CheckerAuthorizationRequirement requirement) { // 从DI获取检查器实例 if (_serviceProvider.GetService(requirement.CheckerType) is IEndpointAuthorizationChecker checker) { var result = await checker.CheckAuthorizationAsync(context); if (result) { context.Succeed(requirement); } else { context.Fail(); } } else { // 找不到检查器实例,直接拒绝授权 context.Fail(); } } }
5. 注册服务到DI容器
在Program.cs中完成服务注册:
builder.Services.AddAuthorization(); // 替换默认的PolicyProvider builder.Services.AddSingleton<IAuthorizationPolicyProvider, CheckerBasedPolicyProvider>(); // 注册自定义Handler builder.Services.AddSingleton<IAuthorizationHandler, CheckerAuthorizationHandler>(); // 注册具体的端点授权检查器示例 builder.Services.AddScoped<IEndpointAuthorizationChecker, OrderEndpointChecker>(); builder.Services.AddScoped<IEndpointAuthorizationChecker, UserEndpointChecker>();
6. 在端点上使用自定义属性
在Controller Action或Minimal API端点上直接标记:
[ApiController] [Route("api/orders")] public class OrdersController : ControllerBase { [HttpGet] [AuthorizeWithChecker(typeof(OrderEndpointChecker))] public IActionResult GetOrders() { // 业务逻辑 return Ok(); } } // 示例检查器实现 public class OrderEndpointChecker : IEndpointAuthorizationChecker { private readonly IUserService _userService; public OrderEndpointChecker(IUserService userService) { _userService = userService; } public async Task<bool> CheckAuthorizationAsync(AuthorizationHandlerContext context) { // 编写该端点的个性化授权逻辑 var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; return await _userService.HasOrderAccessAsync(userId); } }
注意事项
- 所有检查器实现类必须注册到DI容器,否则Handler无法获取实例
- Minimal API中使用方式:
app.MapGet("/api/users", () => Results.Ok()) .RequireAuthorization(new AuthorizeWithCheckerAttribute(typeof(UserEndpointChecker)));
内容的提问来源于stack exchange,提问作者kkdeveloper7
相关产品推荐
相关产品推荐

