如何在 DI 中注册自定义 UserStore 和 UserManager

Posted

技术标签:

【中文标题】如何在 DI 中注册自定义 UserStore 和 UserManager【英文标题】:How to register custom UserStore & UserManager in DI 【发布时间】:2015-08-07 20:00:00 【问题描述】:

这是我的设置:

public class ApplicationUser : IdentityUser<Guid>


public class ApplicationRole : IdentityRole<Guid>


public class ApplicationUserLogin : IdentityUserLogin<Guid>


public class ApplicationUserClaim : IdentityUserClaim<Guid>


public class ApplicationRoleClaim : IdentityRoleClaim<Guid>


这是我的 UserStore 的定义

public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, MyContext, Guid>

    public ApplicationUserStore(MyContext context, IdentityErrorDescriber describer = null)
        : base(context, describer)
    
    

这是我的 UserManager 的定义

public class ApplicationUserManager : UserManager<ApplicationUser>

    public ApplicationUserManager(IUserStore<ApplicationUser> store, IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<ApplicationUser> passwordHasher, IEnumerable<IUserValidator<ApplicationUser>> userValidators,
        IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators, ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors, IEnumerable<IUserTokenProvider<ApplicationUser>> tokenProviders,
        ILoggerFactory logger, IHttpContextAccessor contextAccessor)
        : base(
            store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors,
            tokenProviders, logger, contextAccessor)
    
    

这是我的 DbContext 的定义:

public class MyContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>

    protected override void OnModelCreating(ModelBuilder builder)
    
        base.OnModelCreating(builder);
    

这是我的 Startup.cs

    public IServiceProvider ConfigureServices(IServiceCollection services)
    
        services.AddMvc();

        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.Get("Data:DbConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<MyContext, Guid>()
            .AddUserStore<ApplicationUserStore>()
            .AddRoleStore<ApplicationRoleStore>()
            .AddUserManager<ApplicationUserManager>()
            .AddRoleManager<ApplicationRoleManager>()
            .AddDefaultTokenProviders();

        var builder = new ContainerBuilder();
        builder.Populate(services);
        var container = builder.Build();
        return container.Resolve<IServiceProvider>();
    

这个构造函数的依赖会起作用:

public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager)

这个不会:

public AccountController(ApplicationUserManager userManager, SignInManager<ApplicationUser> signInManager)

有人知道我做错了什么吗?

【问题讨论】:

您为 Microsoft.AspNet.Identity.Core 引用了哪个 nuget 包版本?我正在尝试创建一个 CustomUsermanager,但是当我从 UserManager 继承时,构造函数要求 9 个参数,但您的构造函数需要 10 个参数..?我正在使用 AspDotNetCoreFullFramework。 IUserTokenProvider 无法识别! 【参考方案1】:

DI 通常用于接口驱动的开发; .AddUserManager&lt;ApplicationUserManager&gt;() 指定实现UserManager&lt;&gt;,而不是服务接口。这意味着它仍然希望您获得UserManager&lt;ApplicationUser&gt; 并且只能以这种方式使用它;它会给你一个ApplicationUserManager

我假设您想在ApplicationUserManager 上使用其他方法。 如果不是, 按照它的工作方式使用依赖构造器,享受接口驱动的开发。如果是,你有3个选择:

    通过组合而不是继承使用扩展。 与其从UserManager&lt;&gt; 继承,不如将ApplicationUserManager 编写为包装类;您可以将其包含在构造函数中。这应该会为您提供 ApplicationUserManager 中所需的所有功能。

    您自己将其按原样添加到 DI 框架中。这并不像听起来那么困难,因为 UserManager&lt;&gt; 本身没有真实状态:

    services.AddScoped<ApplicationUserManager>();
    

    这里的缺点是您实际上将有两个UserManager&lt;&gt; 对象用于用户范围;结果可能会导致一些效率低下。从当前代码的状态来看,我认为不是。

    将其编写为扩展方法。如果您有许多依赖项,而不仅仅是 UserManager&lt;&gt; 的基本功能,这可能非常复杂。

    李>

【讨论】:

完全正确,我实际上要修复这个“很快”github.com/aspnet/Identity/issues/493【参考方案2】:

我现在使用的是 ASP.NET Core 1.1,此行为已得到修复。

我可以轻松实现自己的 UserManager 和 UserStore,然后按以下方式引导应用程序:

// identity models
services
    .AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
    .AddUserManager<ApplicationUserManager>()
    .AddUserStore<ApplicationUserStore>()
    .AddDefaultTokenProviders();

并将 UserManager 和 UserStore 都注入到我的控制器中,没有任何问题:

public AccountController(
    IIdentityServerInteractionService interaction,
    IClientStore clientStore,
    IHttpContextAccessor httpContextAccessor,
    ApplicationUserManager userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory)

    _interaction = interaction;
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
    _account = new AccountService(_interaction, httpContextAccessor, clientStore);

【讨论】:

感谢您的回答 - 让我朝着正确的方向前进!按预期工作。【参考方案3】:

我想出了这个:

// Extract IApplicationUserManager interface with all methods you are using
public class ApplicationUserManager : UserManager<ApplicationUser>, IApplicationUserManager

// Register your custom user manager
services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddUserManager<ApplicationUserManager>();

// Return same scoped instance whenever you injecting your custom user manager into any constructor
services.AddScoped<IApplicationUserManager>(s => s.GetService<ApplicationUserManager>());

【讨论】:

以上是关于如何在 DI 中注册自定义 UserStore 和 UserManager的主要内容,如果未能解决你的问题,请参考以下文章

UserManager.FindAsync 使用自定义 Userstore 的 FindByIdAsync 方法而不是 FindByNameAsync

使用 Ninject OWIN 中间件在 OWIN 启动中注入 UserStore

Asp.Net Core 2.1 Identity - UserStore 依赖注入

blazor wasm 如何从 di 注入自定义类

使用整数用户 ID 扩展 EF Core Identity UserStore

如何创建IOptions 来自T的自定义实例(不是通过DI)