仅授权 ASP.NET Core 中的某些 Http 方法
Authorize only certain Http methods in ASP.NET Core
我想为控制器上的所有操作要求一个策略,我还想为所有对 HTTP 的调用要求第二个策略 "edit methods" (POST, PUT, PATCH,和删除)。也就是说,编辑方法应该需要这两种策略。由于实现要求,以及保持代码 DRY 的愿望,我需要在控制器级别应用后一个策略,而不是在所有操作方法上重复。
举个简单的例子,我有一个PeopleController
,我还有两个权限,实现为策略,ViewPeople
和EditPeople
。现在我有:
[Authorize("ViewPeople")]
public class PeopleController : Controller { }
如何添加 EditPeople
policy/permission 使其 "stacks" 仅适用于编辑动词?
我 运行 遇到了两个看起来都很痛苦的问题:
- 您不能有超过一个 AuthorizeAttribute 或在 AuthorizeAttribute 中指定超过一个策略,AFAIK。
- 你不能在自定义的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
方法并在那里进行授权吗?我想这至少是不受欢迎的?
您可以将 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
{
...
}
我想为控制器上的所有操作要求一个策略,我还想为所有对 HTTP 的调用要求第二个策略 "edit methods" (POST, PUT, PATCH,和删除)。也就是说,编辑方法应该需要这两种策略。由于实现要求,以及保持代码 DRY 的愿望,我需要在控制器级别应用后一个策略,而不是在所有操作方法上重复。
举个简单的例子,我有一个PeopleController
,我还有两个权限,实现为策略,ViewPeople
和EditPeople
。现在我有:
[Authorize("ViewPeople")]
public class PeopleController : Controller { }
如何添加 EditPeople
policy/permission 使其 "stacks" 仅适用于编辑动词?
我 运行 遇到了两个看起来都很痛苦的问题:
- 您不能有超过一个 AuthorizeAttribute 或在 AuthorizeAttribute 中指定超过一个策略,AFAIK。
- 你不能在自定义的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
方法并在那里进行授权吗?我想这至少是不受欢迎的?
您可以将 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
{
...
}