.NET 6控制台无DI使用UserManager生成重置令牌报错求助
.NET 6控制台应用手动构造UserManager生成重置令牌报错:No IUserTwoFactorTokenProvider named 'Default' is registered.
问题描述
在.NET 6控制台应用中尝试不依赖依赖注入(DI)直接构造UserManager<ApplicationUser>实例,成功创建实例后,调用GeneratePasswordResetTokenAsync生成密码重置令牌时抛出错误:
No IUserTwoFactorTokenProvider named 'Default' is registered.
初始化UserManager的代码(Program.cs)
IConfiguration config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddEnvironmentVariables() .Build(); IServiceCollection services = new ServiceCollection(); services.AddTransient<UserManager<ApplicationUser>>(); var connectionStrings = config.GetSection("ConnectionStrings"); var messagingConnectionString = connectionStrings.GetValue<string>("Messaging"); AgentConfigDTO appSettingsDTO = new AgentConfigDTO() { .... }; services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionStrings.ToString(), sqlServerOptionsAction: sqlOptions => { sqlOptions.EnableRetryOnFailure( maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); } )); // Register UserManager & RoleManager services.AddIdentity<IdentityUser, IdentityRole>(); services.AddIdentity<ApplicationUser, IdentityRole>( options => { options.Tokens.ProviderMap.Add("Default", new TokenProviderDescriptor(typeof(IUserTwoFactorTokenProvider<ApplicationUser>))); }) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.AddTransient<IUserStore<IdentityUser>, UserStore<IdentityUser>>(); services.AddTransient<UserManager<IdentityUser>>(); IServiceProvider serviceProvider = services.BuildServiceProvider(); var optionsBuilderApplication = new DbContextOptionsBuilder<ApplicationDbContext>(); var _applicationContext = new ApplicationDbContext(optionsBuilderApplication.UseSqlServer(connectionStrings.ToString()).Options); IUserStore<ApplicationUser> store = new UserStore<ApplicationUser>(_applicationContext); UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>(store, null, new PasswordHasher<ApplicationUser>(), null, null, null, null, serviceProvider, null); // UserManager & RoleManager require logging and HttpContext dependencies services.AddLogging(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); new ReportAgent(appSettingsDTO, messagingConnectionString, userManager).RunAsConsole(args);
调用UserManager的方法代码
public void CheckingPasswords() { try { var date = DateTime.Now.AddMonths(-3); var userList = _applicationContext.Users.Where(t => t.PasswordExpiry != null && t.PasswordExpiry <= DateTime.Now.AddMonths(-3) && t.Deleted == false && t.PortalAccessEnabled == true).ToList(); if (userList.Count > 0) { // Create email foreach (var user in userList) { // Lock user out of the account user.PortalAccessEnabled = false; _applicationContext.Update(user); _applicationContext.SaveChanges(); // Create email // Exception is thrown on this line of code var code = _userManager.GeneratePasswordResetTokenAsync(user).ToString(); var url = ""; // 原代码省略部分 var callbackUrl = url.Replace("[[id]]", user.Id).Replace("[[code]]", code); Email email = new() { ... // 原代码省略部分 }; _dbContext.Add(email); _dbContext.SaveChanges(); } } } }
已尝试的无效操作
手动添加Token Provider但未解决问题:
var identityBuilder = new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole<string>), services); identityBuilder.AddTokenProvider("Default", typeof(DataProtectorTokenProvider<ApplicationUser>)); services.AddTransient<UserManager<ApplicationUser>>();
解决方案
核心问题分析
- 重复注册Identity服务:同时注册
IdentityUser和ApplicationUser的AddIdentity,导致服务冲突,令牌提供器未正确绑定到ApplicationUser。 - 手动构造UserManager缺少必要依赖:实例化时传入的令牌验证器、配置参数等为
null,UserManager无法找到默认令牌提供器。 - 服务提供器构建时机错误:在添加日志、
IHttpContextAccessor等依赖前就构建了ServiceProvider,导致这些服务无法被UserManager使用。 - 异步方法调用错误:直接对
GeneratePasswordResetTokenAsync调用.ToString(),未等待异步完成,既拿不到正确令牌,也可能引发其他问题。
修正步骤
1. 清理并正确注册Identity服务
移除重复的AddIdentity<IdentityUser, IdentityRole>(),只保留ApplicationUser的Identity注册,确保自动添加默认令牌提供器:
// 移除这行无效注册:services.AddIdentity<IdentityUser, IdentityRole>(); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); // 自动注册默认令牌提供器
2. 提前添加必要依赖并正确构建ServiceProvider
在构建ServiceProvider前添加日志、DataProtection等核心依赖(令牌生成依赖DataProtection加密):
// 添加必要服务 services.AddLogging(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.AddDataProtection(); // 令牌加密需要此服务 // 最后构建服务提供器 IServiceProvider serviceProvider = services.BuildServiceProvider();
3. 通过ServiceProvider获取UserManager(推荐)
避免手动构造UserManager,直接从DI容器获取,确保所有依赖都正确注入:
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
4. 修正异步方法调用
将GeneratePasswordResetTokenAsync的调用改为异步等待,同时更新相关操作的异步方法:
// 方法需改为async public async Task CheckingPasswords() { try { var date = DateTime.Now.AddMonths(-3); var userList = _applicationContext.Users.Where(t => t.PasswordExpiry != null && t.PasswordExpiry <= DateTime.Now.AddMonths(-3) && t.Deleted == false && t.PortalAccessEnabled == true).ToList(); if (userList.Count > 0) { foreach (var user in userList) { user.PortalAccessEnabled = false; _applicationContext.Update(user); await _applicationContext.SaveChangesAsync(); // 异步等待获取令牌 var code = await _userManager.GeneratePasswordResetTokenAsync(user); var url = ""; var callbackUrl = url.Replace("[[id]]", user.Id).Replace("[[code]]", code); Email email = new() { ... }; _dbContext.Add(email); await _dbContext.SaveChangesAsync(); } } } }
(可选)如果必须手动构造UserManager
若一定要手动实例化,需传入所有必要依赖并手动注册令牌提供器:
var _applicationContext = new ApplicationDbContext(optionsBuilderApplication.UseSqlServer(connectionStrings.ToString()).Options); IUserStore<ApplicationUser> store = new UserStore<ApplicationUser>(_applicationContext); // 从服务容器获取必要依赖 var identityOptions = serviceProvider.GetRequiredService<IOptions<IdentityOptions>>().Value; var tokenProvider = serviceProvider.GetRequiredService<IUserTwoFactorTokenProvider<ApplicationUser>>(); var logger = serviceProvider.GetRequiredService<ILogger<UserManager<ApplicationUser>>>(); var userValidators = new List<IUserValidator<ApplicationUser>> { new UserValidator<ApplicationUser>() }; var passwordValidators = new List<IPasswordValidator<ApplicationUser>> { new PasswordValidator<ApplicationUser>() }; // 实例化UserManager UserManager<ApplicationUser> userManager = new UserManager<ApplicationUser>( store, identityOptions, new PasswordHasher<ApplicationUser>(), userValidators, passwordValidators, new UpperInvariantLookupNormalizer(), new IdentityErrorDescriber(), serviceProvider, logger); // 手动注册默认令牌提供器 userManager.RegisterTokenProvider("Default", tokenProvider);
内容的提问来源于stack exchange,提问作者Raluca Micu
相关产品推荐
相关产品推荐

