Внедрение зависимостей от требования AuthorizationOptions в ядре DotNet

У меня есть основной проект.NET, и я пытаюсь создать собственную политику, используя AuthorizationOptions, как показано в документации, расположенной здесь:

ASP.NET.Core Authorization - внедрение зависимостей в обработчики требований

В примерах показано, как настроить требование авторизации с 1 параметром - простым значением int. Мое пользовательское требование требует строковый параметр, а также объект DbContext. Я хочу внедрить DbContext в конструктор требования во время выполнения. Я использую контейнер Autofac. Я не уверен, как мне этого добиться - перепробовал несколько подходов, и пока ничего не работает.

Вот мое пользовательское требование:

public UserNameRequirement(string username, MyDbContext context)
{
    _userName = username;
    _dbContext = context;
}

При настройке параметров авторизации в методе Startup.cs ConfigureServices документация показывает, что вы регистрируете это следующим образом:

services.AddAuthorization(options =>
{
    options.AddPolicy(
        "UserNamePolicy",
        policy => policy.Requirements.Add(new UserNameRequirement("admin", ** want to resolve and inject my DbContext here **)));
}

Я не уверен, как этого добиться. Я видел этот пост, который похож на вопрос, но он использует ASP.NET 5, и этот синтаксис не работает с ядром.net:

Внедрение зависимостей от AuthorizationOptions

1 ответ

Решение

Хорошо, я собираюсь сделать предположение здесь, и это то, что вам нужно внедрить экземпляр MyDbContext в UserNameRequirement выполнить бизнес-логику.

Если это так, то это означает, UserNameRequirement оба хранят данные - в вашем случае имя пользователя - и выполняют логику авторизации. Примером этого в ASP.NET Core является ClaimsAuthorizationRequirement,

Решение этой проблемы состоит в том, чтобы разделить это на два класса - с одной стороны требование, которое просто содержит данные, связанные с требованием, с другой - обработчик авторизации. Как примечание, даже если мы пройдем через это, то, что я описываю, доступно в официальных документах ASP.NET Core.

Таким образом, класс требований может выглядеть примерно так:

public class UserNameRequirement : IAuthorizationRequirement
{
    public class UserNameRequirement(string userName)
    {
        UserName = userName;
    }

    public string UserName { get; }
}

и класс обработчика будет:

public class UserNameRequirementHandler : AuthorizationHandler<UserNameRequirement>
{
    private readonly MyDbContext _dbContext;

    public UserNameRequirementHandler(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNameRequirementHandler requirement)
    {
        var userName = requirement.UserName;

        // Use _dbContext to perform business logic
    }
}

Следующая и последняя часть состоит в том, чтобы зарегистрировать обработчик в контейнере:

services.AddSingleton<IAuthorizationHandler, UserNameRequirementHandler>();

В результате вы можете добавить свое требование в политику, не беспокоясь о DbContext:

services.AddAuthorization(options =>
{
    options.AddPolicy(
        "UserNamePolicy",
        policy => policy.Requirements.Add(new UserNameRequirement("admin")));
}

Затем ASP.NET внутренне разрешит все обработчики, связанные с этим требованием, через контейнер, поэтому экземпляр MyDbContext будет доступен вам в обработчике, что позволит вам выполнять бизнес-логику по своему усмотрению.

Надеюсь, мое предположение верно, и это поможет вам.

Редактировать:

Генри Ру в своем комментарии высказал хорошую мысль о том, что если UserNameRequirementHandler зарегистрирован как одиночный, а затем один экземпляр MyDbContext будет использоваться, и это может привести к проблемам. Убедитесь, что вы зарегистрировали свои обработчики авторизации с соответствующим жизненным циклом.

Вы также можете использовать метод GetRequiredService:

public class ExampleRequirement : AuthorizationHandler<ExampleRequirement>, IAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ExampleRequirement requirement)
    {
        UserManager<ApplicationUser> UserManager = ((ActionContext)context.Resource).HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();

        // you can work with the users ...      

        return Task.CompletedTask;
    }
}
Другие вопросы по тегам