对DotNet Core中AuthorizationOptions要求的依赖注入

Pac*_*der 10 dependency-injection autofac asp.net-authorization .net-core asp.net-core

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

ASP.NET.Core授权 - 需求处理程序中的依赖注入

这些示例显示了使用1个参数设置授权要求 - 一个简单的int值.我的自定义要求需要字符串参数以及DbContext对象.我想在运行时将DbContext注入需求的构造函数中.我正在使用Autofac容器.我不确定如何实现这一点 - 尝试了几种方法,到目前为止还没有任何工作.

这是我的自定义要求:

public UserNameRequirement(string username, MyDbContext context)
{
    _userName = username;
    _dbContext = context;
}
Run Code Online (Sandbox Code Playgroud)

在Startup.cs ConfigureServices方法中设置授权选项时,文档显示您如下所示注册:

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

我不知道如何实现这一目标.我看过这篇文章是一个类似的问题,但它使用的是ASP.NET 5,并且该语法不适用于.net核心:

AuthorizationOptions上的依赖注入

Mic*_*iey 13

好的,我将在这里做一个假设,那就是你需要注入一个MyDbContextin 实例UserNameRequirement来执行业务逻辑.

如果是这种情况,那么它意味着UserNameRequirement两者都保存数据 - 在您的情况下是用户名 - 并且执行授权逻辑.ASP.NET Core中的一个例子是ClaimsAuthorizationRequirement.

对此的解决方案是将其分为两类 - 一方面是仅保存与需求相关联的数据的需求,另一方面是授权处理程序.作为一个注释,即使我们将通过它,我正在描述的官方ASP.NET核心文档中提供了.

所以需求类看起来像这样:

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

    public string UserName { get; }
}
Run Code Online (Sandbox Code Playgroud)

并且处理程序类将是:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

下一个和最后一个部分是在容器中注册处理程序:

services.AddSingleton<IAuthorizationHandler, UserNameRequirementHandler>();
Run Code Online (Sandbox Code Playgroud)

这样做的效果是您现在可以将您的需求添加到策略中而无需担心DbContext:

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

在内部,ASP.NET将通过容器解析与该需求相关联的所有处理程序,因此MyDbContext您可以在处理程序中使用该实例,从而允许您根据需要执行业务逻辑.

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

编辑:

Henry Roux在下面的评论中提出了一个很好的观点,即如果UserNameRequirementHandler注册为单身,那么MyDbContext将使用单个实例,这可能会导致问题.确保使用适当的生命周期注册授权处理程序.

  • 我对这个解决方案在负载测试和实际生产环境中取得多大成功感兴趣?`UserNameRequirementHandler`被注册为单例,这意味着注入那里的`MyDbContext`永远不会刷新.因此它永远不会被处理,连接永远不会被关闭,如果你不使用`AsNoTracking()`选项,你会发现当你从该实体读取时,数据库中更新的数据永远不会被正确报告.有经验的这个? (3认同)
  • @zemien并非如此,它将UserNameRequirementHandler链接为IAuthorizationHandler的一个实例,而不是单个实例。细微的区别是,您可以在同一个IAuthorizationHandler接口上注册多个类型或实例,并且框架将解析所有类型或实例。我建议您阅读[官方文档](https://docs.microsoft.com/zh-cn/aspnet/core/security/authorization/policies#authorization-handlers)了解更多信息。 (2认同)