如何在ASP.NET Core 2.2中正确执行基于资源的授权?

时间:2019-08-12 17:10:17

标签: asp.net-core authorization

我有一个ASP.NET Core 2.2应用程序,其中正在显示一些文档。大多数文档是公开的,因此可以匿名访问。但是,某些文档是私有的(即它们需要身份验证/授权),将来某些文档可能还需要有效的订阅。使用相同的操作检索所有文档,因此我们仅在加载文档后知道所需的权限。我们还将某些资源作为静态文件(IApplicationBuilder.UseStaticFiles加载,但是我想这应该不是一个问题,因为StaticFileOptions.OnPrepareResponse可以用于自定义授权代码。

当前谁可以访问私有文档的逻辑非常简单。目前,我们仅显示文档,不允许对其进行任何其他类型的操作(编辑,删除等)。对我来说,这听起来像是基于资源的授权的标准案例。

无论如何,我发现了this article,根据我的理解,我需要定义一个策略(由魔术字符串标识-这是怎么回事?!),需求和{ 1}},它将执行实际的授权逻辑。然后,在控制器操作中,我将需要调用AuthorizationHandler<MyRequirement, MyResource>并向用户传递资源和策略名称(魔术字符串),并根据该方法的结果,允许或拒绝访问。对于我要完成的工作,这似乎使我感到困惑。如果我简单地定义自己的“授权服务”并简单地删除整个策略和要求之类的东西,可能会更容易。我还认为,必须在所有受影响的控制器操作中复制if-else逻辑,这并不理想。

当然我不是唯一一个遇到此问题的人。有什么我想念的吗? 如果确实有充分的理由使用策略和要求,那么在这种情况下如何命名?我真的有点失落。 使用文档类型(公共,私有,仅订阅者)作为策略名称也许有意义?

2 个答案:

答案 0 :(得分:0)

我建议本文中介绍的最后一种方法-https://www.red-gate.com/simple-talk/dotnet/c-programming/policy-based-authorization-in-asp-net-core-a-deep-dive/

仅使用带有策略名称的注释即可使控制器保持清洁。在处理程序中,您必须实现逻辑检查人员是否可以访问资源-例如,它可以基于检查资源中的属性ownerId(例如,在数据库表列中)或AD中某个组的成员,或执行任何操作其他。

编辑:

使用Requirements和RequirementsHandlers-我做了类似的事情。 我不知道您的逻辑应该如何工作,所以我将假设一些。

让我们说您有一个get端点:documents / documentId

您想应用逻辑,使该文档仅文档所有者可以访问。显然,您需要在某个地方存储谁是文档的所有者,因此请将其保留在文档实体的属性中。

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IsDocumentOwner requirement, DocumentRepository documentRepository)
{
    if (context.Resource is AuthorizationFilterContext ctx)
    {
        var documentId = ctx.RouteData.Values["documentId"]?.ToString();

        //here load document from repo and check if the property ownerId is equal to current user id

        var userId = context.User.Claims.FirstOrDefault(x => x.ToString().Contains(oid))?.Value;
        //if yes, make the request pass to the body of a controller with the attribute
        context.Succeed(requirement);
    }

}

实现IsDocumentOwner:

public class IsDocumentOwner : IAuthorizationRequirement 
    {
    }

在您的Startup.cs中添加:

services.AddAuthorization(options =>
            {
                options.AddPolicy(
                    nameof(IsDocumentOwner),
                    policyBuilder => policyBuilder.AddRequirements(
                        new IsDocumentOwner()));
            });

然后,最后一步,将属性应用于控制器方法

[Authorize(Policy = "IsDocumentOwner")]
[HttpGet("{documentId}")]
public YourDocumentObjectResultClass GetDocument([FromRoute]string documentId)
{
//stuff you do when current user is owner of the document, probably just display the doc
}

对于您的IsDocumentOwner处理程序,您可以按构造函数注入任何服务(由上面的存储库可视化),例如,检查用户是否是天蓝色广告组的成员

答案 1 :(得分:0)

最后,我们不想处理这些东西,只是编写了自己的AuthorizationService,它像任何其他服务一样注入到控制器中。 它将在首次使用时为所有文档加载所需的权限并对其进行缓存。 我们的控制器方法如下所示:

    [HttpGet("[action]")]
    public async Task<Document> GetDocument(string documentId)
    {
        if (_authorizationService.MayAccess(User, documentId))
        {
            return _documentRepository.GetDocument(documentId);
        }
        else
        {
            Response.StatusCode = StatusCodes.Status403Forbidden;
            return null;
        }
    }