DotNet Core 中 AuthorizationOptions 要求的依赖注入

Posted

技术标签:

【中文标题】DotNet Core 中 AuthorizationOptions 要求的依赖注入【英文标题】:Dependency Injection on AuthorizationOptions Requirement in DotNet Core 【发布时间】:2017-07-14 09:05:46 【问题描述】:

我有一个 .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 **)));

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

Dependency Injection on AuthorizationOptions

【问题讨论】:

请提供Minimal, Complete, and Verifiable example。 出于好奇,在UserNameRequirement 类中需要MyDbContext 的实例吗? 【参考方案1】:

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

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

对此的解决方案是将其分为两个类 - 一方面是仅保存与需求相关联的数据的需求,另一方面是授权处理程序。请注意,即使我们会仔细阅读,我所描述的内容也可以在 official ASP.NET Core docs 中找到。

所以需求类可能看起来像:

public class UserNameRequirement : IAuthorizationRequirement

    public 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, 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 的单个实例,这可能会导致问题。确保使用适当的生命周期注册授权处理程序。

【讨论】:

谢谢!这正是我需要的明确解释。 我对这个解决方案在负载测试和实际生产环境中的成功程度很感兴趣? UserNameRequirementHandler 被注册为单例,这意味着在那里注入的 MyDbContext 永远不会刷新。因此它永远不会被释放,连接永远不会关闭,如果你不使用AsNoTracking() 选项,你会发现当你从该实体读取数据时,数据库中更新的数据永远不会正确报告。经历过这些吗? @HenkRoux 这是一个好点。我将尝试通过说答案是在这里提供一种使用 DI 满足需求的方法来证明这一点,而不是唯一的方法。我将编辑答案以包括您的 cmets。干杯。 @MickaëlDerriey 如果我有多个需要 DI 的处理程序怎么办? sn-p services.AddSingleton&lt;IAuthorizationHandler, UserNameRequirementHandler&gt;(); 清楚地将所有 IAuthorizationHandlers 链接到该单个实例... @zemien 不是真的,它将UserNameRequirementHandler 链接为IAuthorizationHandler一个 实例,而不是单个 实例。细微的差别是您可以针对同一个IAuthorizationHandler 接口注册多个类型或实例,框架将解析所有这些。我建议您阅读official documentation 了解更多信息。【参考方案2】:

您也可以使用 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;
    

【讨论】:

以上是关于DotNet Core 中 AuthorizationOptions 要求的依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

Core dotnet 命令大全

DotNet Core 中 AuthorizationOptions 要求的依赖注入

如何在 dotnet core 中制作自定义身份验证过滤器,如 Iauthentication

dotNet Core 3.1 使用 Aspose (部署 Docker)

dotnet core 创建文件名中没有“CoreFxPipe_”的命名管道

如何正确停止运行 dotnet core web 应用程序?