Внедрение зависимостей от требования 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:
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;
}
}