You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6控制台应用密码重置报错:未注册'Default'的IUserTwoFactorTokenProvider

解决.NET 6控制台应用中GeneratePasswordResetTokenAsync的"Default" TokenProvider未注册问题

问题根源分析

你遇到的错误核心是手动实例化UserManager时缺失TokenProvider依赖,再加上Identity服务注册混乱,导致系统找不到名为"Default"的令牌提供者:

  1. 重复注册AddIdentity<IdentityUser, IdentityRole>()和AddIdentity<ApplicationUser, IdentityRole>(),造成配置冲突
  2. 手动new UserManager<ApplicationUser>()时,未传入令牌提供者、日志等必要依赖
  3. 错误直接调用GeneratePasswordResetTokenAsync(user).ToString(),未await异步方法,得到的是Task对象字符串而非实际令牌

具体修复步骤

1. 清理并正确注册Identity服务

删除重复的Identity注册,只保留针对ApplicationUser的配置。AddDefaultTokenProviders()已经默认注册了名为"Default"的DataProtectorTokenProvider,无需手动添加ProviderMap:

// 移除以下重复注册代码
// services.AddIdentity<IdentityUser, IdentityRole>();
// services.AddTransient<IUserStore<IdentityUser>, UserStore<IdentityUser>>();
// services.AddTransient<UserManager<IdentityUser>>();

// 仅保留ApplicationUser的Identity配置
services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders(); // 该行已自动注册Default令牌提供者

2. 通过依赖注入获取UserManager,禁止手动实例化

手动new的UserManager缺少大量必要依赖(包括TokenProvider),必须从ServiceProvider获取:

// 先添加必要依赖再构建ServiceProvider
services.AddLogging();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

// 构建服务容器
IServiceProvider serviceProvider = services.BuildServiceProvider();

// 从容器获取UserManager,而非手动实例化
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();

3. 修正异步方法调用

GeneratePasswordResetTokenAsync是异步方法,必须用await获取结果,同时将相关方法改为异步:

// 将CheckingPasswords改为异步方法
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()
                {
                    ID = 0,
                    From = _appConfig.DefaultFromEmail,
                    To = user.Email,
                    IsHTML = true,
                    QueuedAt = DateTime.Now,
                    SendAt = DateTime.Now,
                    Subject = Core.Resource.Portal.Pages.Common_PortalName + " - " + Core.Resource.Portal.Pages.User_ResetPassword_EmailSubject,
                    Created = DateTime.Now,
                    Body = string.Format(Core.Resource.Portal.Pages.User_ResetPassword_EmailBody, callbackUrl),
                    TrackingID = Guid.NewGuid(),
                    Status = 1 //Pending
                };
                _dbContext.Add(email);
                await _dbContext.SaveChangesAsync(); // 同步方法改为异步
            }
        }
    }
    catch(Exception ex)
    {
        // 建议添加异常处理逻辑
    }
}

// 将Run方法改为异步
public async Task Run()
{
    await CheckingPasswords();
}

4. 调整ReportAgent中的异步调用

因为Run方法改为异步,需要在调用处使用await:

protected async Task RunSystemJob(string jobName, string parameters, int commandTimeOutSecs, AgentConfigDTO _appConfig)
{
    IReportJob job = null;

    switch (jobName.ToLower())
    {
        case "checkpasswords":
            job = new CheckPasswords(_context, parameters, _applicationContext, _userManager, _appConfig);
            break;

        default:
            throw new Exception($"Cannot execute system job {jobName} as it cannot be found.");
    }

    if (job != null)
    {
        job.CommandTimeOut = commandTimeOutSecs;
        job._appConfig = _appConfig;
        await ((dynamic)job).Run(); // 或让IReportJob接口定义异步Run方法
    }
}

额外注意事项

  • 确保ApplicationUser继承自IdentityUser,否则Identity服务无法正确识别
  • 控制台应用中使用Identity必须添加IHttpContextAccessor,部分内部逻辑依赖该服务
  • 数据库操作尽量使用异步方法,避免阻塞线程

内容的提问来源于stack exchange,提问作者Raluca Micu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 00:30:38