Глобальная авторизация ресурсов на основе значения маршрута - PullRequest
3 голосов
/ 05 марта 2020

Я работаю над ASP. Net Core 3.1 веб-API.

Пользователи API из Azure AD. Пользователи могут получить доступ к API, если у них есть лицензия, каждому пользователю может быть назначено несколько лицензий, и одна и та же лицензия может быть назначена нескольким пользователям.

Клиент хочет, чтобы я структурировал маршруты API с помощью шаблона, подобного <api_url>/{licenseId}/controller/action.

Мои контроллеры похожи на:

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

Если я рассматриваю Лицензию как Ресурс, я могу использовать авторизацию на основе Ресурса, как подробно здесь, Это работает, но я копирую и вставляю проверку подлинности для всех действий.

Есть ли лучший способ авторизации пользователя с использованием значений маршрута?

Вот что у меня получилось far:

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 Ответ

1 голос
/ 06 марта 2020

Я нашел 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
{
    [...]
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...