基于路由值的全局资源授权

时间:2020-03-05 20:22:48

标签: c# asp.net-core routes asp.net-core-webapi asp.net-authorization

我正在使用ASP.Net Core 3.1 Web API。

API用户来自Azure AD。 如果用户具有许可证,则可以访问API,可以将每个用户分配给多个许可证,并且可以将同一许可证分配给多个用户。

客户端希望我使用<api_url>/{licenseId}/controller/action之类的模板来构造API路由。

我的控制器就像:

[Authorize]
[Route("{licenseId}/Foo")]
public class FooController : ControllerBase
{

如果我将许可证视为资源,则可以使用基于资源的授权,详细说明here。 它可以工作,但是我发现自己复制并粘贴了所有操作的身份验证检查。

是否有更好的方法来使用路由值授权用户?

这是到目前为止我得到的:

public class LicenseRequirement : IAuthorizationRequirement
{        
    public Guid LicenseId { get; private set; }

    public LicenseRequirement(Guid licenseId)
    {
        LicenseId = licenseId;
    }
}

public class LicenseAuthorizationHandler : AuthorizationHandler<LicenseRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ILogger<LicenseAuthorizationHandler> _logger;
    private readonly DBContext _db;

    public LicenseAuthorizationHandler(DBContext context, ILogger<LicenseAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
    {
        _logger = logger;
        _db = context;
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, LicenseRequirement requirement)
    {
        var userId = new Guid(context.User.GetUserId());
        var licenseId = _httpContextAccessor.HttpContext.GetRouteData().Values["licenseId"];

        if (await _db.ApiUsers.SingleOrDefaultAsync(x => x.LicenseId == new Guid(licenseId as string) && x.UserId == userId) is ApiUser user)
            context.Succeed(requirement);
    }
}

现在,我有点不知所措,因为我不知道如何在Startup.cs中进行设置并将其用作属性或区域过滤器,并在运行时使用licenseId路由的值来创建这些要求

1 个答案:

答案 0 :(得分:1)

我发现IAuthorizationFilter非常容易实现。昨天尝试时,我找不到RouteValues,但它们在那里:

public class LicenseAuthorizationFilter : IAuthorizationFilter
{
    private readonly ILogger<LicenseAuthorizationFilter> _logger;
    private readonly DBContext _db;

    public LicenseAuthorizationFilter(DBContext context, ILogger<LicenseAuthorizationFilter> logger)
    {
        _logger = logger;
        _db = context;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var userId = new Guid(context.HttpContext.User.GetUserId());
        var licenseId = new Guid(context.HttpContext.Request.RouteValues["licenseId"] as string);

        if (!(_db.ApiUsers.SingleOrDefault(x => x.LicenseId == licenseId && x.UserId == userId) is ApiUser user))
        {
            context.Result = new ForbidResult();
        }
    }
}

public class LicenseAuthorizationAttribute : TypeFilterAttribute
{
    public LicenseAuthorizationAttribute() : base(typeof(LicenseAuthorizationFilter))
    { }
}

控制器可以变得整洁:

[Authorize]
[LicenseAuthorization]
[Route("{licenseId}/Items")]
public class ItemsController : ControllerBase
{
    [...]
}