仅授权 ASP.NET Core 中的某些 Http 方法

Authorize only certain Http methods in ASP.NET Core

我想为控制器上的所有操作要求一个策略,我还想为所有对 HTTP 的调用要求第二个策略 "edit methods" (POST, PUT, PATCH,和删除)。也就是说,编辑方法应该需要这两种策略。由于实现要求,以及保持代码 DRY 的愿望,我需要在控制器级别应用后一个策略,而不是在所有操作方法上重复。

举个简单的例子,我有一个PeopleController,我还有两个权限,实现为策略,ViewPeopleEditPeople。现在我有:

[Authorize("ViewPeople")]
public class PeopleController : Controller { }

如何添加 EditPeople policy/permission 使其 "stacks" 仅适用于编辑动词?

我 运行 遇到了两个看起来都很痛苦的问题:

我尝试使用自定义 Requirement 和 AuthorizationHandler 解决前者问题,如下所示:

public class ViewEditRolesRequirement : IAuthorizationRequirement
{
    public ViewEditRolesRequirement(Roles[] editRoles, Roles[] viewRoles)
        => (EditRoles, ViewRoles) = (editRoles, viewRoles);

    public Roles[] EditRoles { get; }
    public Roles[] ViewRoles { get; }
}

public class ViewEditRolesHandler : AuthorizationHandler<ViewEditRolesRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ViewEditRolesRequirement requirement)
    {
        if (context.User != null)
        {
            var canView = requirement.ViewRoles.Any(r => context.User.IsInRole(r.ToString()));
            var canEdit = requirement.EditRoles.Any(r => context.User.IsInRole(r.ToString()));
            if (context. // Wait, why can't I get to the bloody HttpRequest??
        }
        return Task.CompletedTask;
    }
}

...但在我意识到我无法访问请求对象之前,我已经达到了 if (context.

我唯一的选择是覆盖控制器中的 OnActionExecuting 方法并在那里进行授权吗?我想这至少是不受欢迎的?

您可以将 IHttpContextAccessor 注入您的处理程序并在 HandleRequirementAsync:

中使用它
public class ViewEditRolesHandler : AuthorizationHandler<ViewEditRolesRequirement>
{

    private readonly IHttpContextAccessor _contextAccessor;

    public ViewEditRolesHandler(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ViewEditRolesRequirement requirement)
    {
        if (context.User != null)
        {
            var canView = requirement.ViewRoles.Any(r => context.User.IsInRole(r.ToString()));
            var canEdit = requirement.EditRoles.Any(r => context.User.IsInRole(r.ToString()));
            if (_contextAccessor.HttpContext.Request. // Now you have it!
        }
        return Task.CompletedTask;
    }
}   

You can't access the Request in a custom AuthorizationHandler, so I can't check the HttpMethod...

其实我们can access the Request in an AuthorizationHandler。我们通过使用 as 关键字强制转换 context.Resource 来做到这一点。这是一个例子:

services.AddAuthorization(config =>
{
    config.AddPolicy("View", p => p.RequireAssertion(context =>
    {
        var filterContext = context.Resource as AuthorizationFilterContext;
        var httpMethod = filterContext.HttpContext.Request.Method;
        // add conditional authorization here
        return true; 
    }));

    config.AddPolicy("Edit", p => p.RequireAssertion(context =>
    {
        var filterContext = context.Resource as AuthorizationFilterContext;
        var httpMethod = filterContext.HttpContext.Request.Method;
        // add conditional authorization here
        return true;
    }));
});

You can't have more than one AuthorizeAttribute....

其实我们可以有多个AuthorizeAttribute。注自docs that the attribute has AllowMultiple=true。这使我们能够 "stack" 他们。这是一个例子:

[Authorize(Policy="View")]
[Authorize(Policy="Edit")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    ...
}