【问题标题】:Authorize only certain Http methods in ASP.NET Core仅授权 ASP.NET Core 中的某些 Http 方法
【发布时间】:2018-08-22 06:57:04
【问题描述】:

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

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

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

如何添加 EditPeople 策略/权限,使其“堆叠”并仅适用于编辑动词?

我遇到了两个似乎都很痛苦的问题:

  • 在 AuthorizeAttribute、AFAIK 中不能有多个 AuthorizeAttribute 或多个 Policy。
  • 在自定义的AuthorizationHandler中无法访问Request,所以无法查看HttpMethod来查看。

我尝试使用自定义 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 方法并在那里进行授权吗?我想这至少是不被接受的?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-identity asp.net-authorization


    【解决方案1】:

    你无法在自定义 AuthorizationHandler 中访问 Request,所以我无法检查 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;
        }));
    });
    

    您不能拥有多个 AuthorizeAttribute....

    实际上,我们可以拥有多个 AuthorizeAttribute。来自docs that the attribute has AllowMultiple=true 的注释。这使我们能够“堆叠”它们。这是一个例子:

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

    【讨论】:

    • 优秀。至于多个问题,我确定我看到的错误消息说 AllowMultiple=false;直到现在我才费心去查看 GitHub 上的源代码,因为消息很清楚。我必须弄清楚为什么我看到了我所看到的。谢谢。
    • 也就是说,我意识到这是解决问题的捷径,但在这里寻求建议..这是完成我想做的事情的最佳方式吗?我觉得我正在做的事情真的很令人费解。在 MVC 6 中做同样的事情只需要 10 行自定义属性中的代码。建议/想法?
    • 在我的脑海中,我不知道有更好的方法来完成你想做的事情。我只能建议在这里深入阅读授权文档:docs.microsoft.com/en-us/aspnet/core/security/authorization/…
    • @ShaunLuttin 您知道这些策略是否可以应用于特定的处理程序方法吗?我一直在尝试类似于您在此处显示的内容,但没有成功。文档说对于方法限制,使用过滤器,但我认为,为每页编写一个过滤器而不是所有过滤器的整体 BasePageModel 是不建议的。
    • @B.León 我完全不知道。
    【解决方案2】:

    您可以将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;
        }
    }   
    

    【讨论】:

    • 哇,太好了!我意识到这是解决问题的捷径,但在这里寻求建议..这是完成我想做的事情的最佳方式吗?我觉得我正在做的事情真的很令人费解。在 MVC 6 中做同样的事情只需要在自定义属性中编写 10 行代码。
    猜你喜欢
    • 2021-02-28
    • 2020-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2020-09-25
    • 1970-01-01
    • 2018-06-02
    相关资源
    最近更新 更多