可以禁用组时的扩展授权

Extended authorization when group can be disabled

我正在编写 .net core 3.1 API 应用程序并在应用程序中实现了授权。它是通过 IPolicyRequirement 和 AuthorizationHandler 完成的。

services.AddAuthorizationCore(options =>
                              {
                                  AddPolicy(options, new UserRequirement());
                              });

services.AddTransient<IAuthorizationHandler, UserHandler>();

并且需要的控制器用 Authorize 属性修饰。

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

一切正常,并在指定的地方限制对控制器调用的访问。

现在我需要根据是否启用帐户所属的团队来限制访问。因此,如果团队被禁用,人们可以登录,但无法访问大多数端点。

他们仍然应该能够访问一些端点,否则他们将无法执行业务指定的操作,以允许重新启用团队。不允许任何角色调用该端点 - 仅由授权策略指定。

我也可以通过政策要求做到这一点,但现在我想要一个带有 403 的特定错误消息,因此,API 调用者可以将基于角色的 403 与团队禁用的 403 区分开来。我认为 403对于这两种情况都是理想的代码,因为在这两种情况下都禁止访问指定的端点,在某些情况下只是出于与角色无关的原因。

所以,总而言之,我需要 return 403 带有政策要求的消息,例如:

public class UserHandler : AuthorizationHandler<UserRequirement>
{
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                         UserRequirement requirement)
    {
        var authenticatedUser = GetUserFromClaims();
        if (!await TeamIsEnabled(authenticatedUser))
        {
            context.Resource.Result = new JsonResult("Custom message");
            return;
        }

        if (!await RoleIsAllowed())
            return;

        context.Succeed(requirement);
    }
}

上面的代码不起作用,因为 content.Resource 是 RouteEndpoint 类型并且没有要发送的结果 属性。

我的问题是:

我应该使用中间件和 IAsyncResultFilter 吗?我可以使用它,但我想我应该问问社区我是否可以逃脱

谢谢。

所以,最后我将它们分成 2 位:

  • 实现 IAsyncAuthorizationFilter 以处理组 on/off、returns 400 的过滤器,其中包含错误详细信息,因为我无法将过滤器设置为带有消息的 return 403。
  • 实现 AuthorizationHandler 以限制对特定用户的访问和要求实现 IPolicyRequirement 的处理程序。

startup.cs 中我们有:

services.AddControllers(opt =>
{
 ...
 opt.Filters.Add<GroupIsEnabledFilter>();
 ...
});

services.AddAuthorizationCore(options =>
{
 AddPolicy(options, new MaintainerRequirement());
 AddPolicy(options, new SuperUserRequirement());
})
...
;

services.AddTransient<IAuthorizationHandler, MaintainerHandler>();
services.AddTransient<IAuthorizationHandler, SuperUserHandler>();

过滤器模板可以是这样的:

public class GroupIsEnabledFilter : IAsyncAuthorizationFilter
{
  public GroupIsEnabledFilter(...)
  {
      ...
      //inject your dependencies here
      ...
  }
  
  public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
  {
    if (AllowsDisabledGroup(context)) //use context.ActionDescriptor.EndpointMetadata to get endpoint details
        return;
  
    var group = _authService.GetGroupId();
    if (await GroupIsActive(group))
        return;
  
    var problemDetails = new ProblemDetails
    {
    ...
    };
  
    context.Result = new ObjectResult(problemDetails)
    {
        ContentTypes = new MediaTypeCollection { new MediaTypeHeaderValue("application/json") },
        StatusCode = StatusCodes.Status400BadRequest
    };
  }
}