DotNet Core 中 AuthorizationOptions 要求的依赖注入

Dependency Injection on AuthorizationOptions Requirement in DotNet Core

我有一个 .NET 核心项目,我正在尝试使用 AuthorizationOptions 创建自定义策略,如位于此处的文档所示:

ASP.NET.Core Authorization - Dependency Injection in requirement handlers

示例显示了使用 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 **)));
}

我不确定如何实现。我看过这个 post 这是一个类似的问题,但它使用的是 ASP.NET 5 并且该语法不适用于 .net core:

OK,我这里做一个假设,就是你需要在UserNameRequirement中注入一个MyDbContext的实例来执行业务逻辑。

如果是这种情况,则意味着 UserNameRequirement 既保存数据(在您的情况下是用户名)又执行授权逻辑。 ASP.NET Core 中的一个例子是 ClaimsAuthorizationRequirement.

对此的解决方案是将其分成两个 classes - 一方面是只保存与需求关联的数据的需求,另一方面是授权处理程序。请注意,即使我们将通过它,我所描述的内容在 official ASP.NET Core docs.

中可用

所以要求 class 可能类似于:

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

    public string UserName { get; }
}

处理程序 class 将是:

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

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

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserNameRequirement 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的实例将在处理程序中可供您使用,让您执行业务你认为合适的逻辑。

希望我的假设是正确的,这对你有帮助。

编辑:

Henry Roux 在下面的评论中提出了一个很好的观点,即如果 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;
    }
}