Asp.Net Identity登录后改密码调用SendTwoFactorCodeAsync报UserId不存在错误
解决Asp.Net Identity修改密码时调用SendTwoFactorCodeAsync出现"UserId not found"的问题
这个问题我之前也碰到过,核心原因是登录流程和修改密码流程中,SignInManager对用户上下文的依赖逻辑不一样:
登录时调用SendTwoFactorCodeAsync是在用户名密码验证通过后,此时SignInManager已经把用户的临时身份信息存在了AuthenticationProperties里,自然能找到用户ID;但修改密码时,用户虽然已经登录,SignInManager的SendTwoFactorCodeAsync默认依赖的是"待完成2FA验证的临时用户上下文",而不是当前已登录的用户上下文,所以会报找不到用户ID的错误。
下面是具体的解决思路和代码示例:
第一步:确保修改密码流程中能正确获取当前用户
在修改密码的Action里,先确认用户已认证,并且能拿到有效用户实体:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model) { if (!ModelState.IsValid) return View(model); // 先检查用户是否已登录 if (!User.Identity.IsAuthenticated) return RedirectToAction("Login", "Account"); // 获取当前登录用户ID并找到用户实体 var userId = _signInManager.GetUserId(User); var user = await _userManager.FindByIdAsync(userId); if (user == null) return View("Error"); // 先验证用户当前密码(修改密码的必要步骤,同时能让SignInManager关联到正确的用户上下文) var passwordCheck = await _signInManager.PasswordSignInAsync( user.UserName, model.CurrentPassword, isPersistent: false, lockoutOnFailure: false ); if (!passwordCheck.Succeeded) { ModelState.AddModelError(string.Empty, "当前密码不正确"); return View(model); } // 检查用户是否启用了2FA if (!await _userManager.GetTwoFactorEnabledAsync(user)) { ModelState.AddModelError(string.Empty, "您未启用双因素认证"); return View(model); }
第二步:替换SendTwoFactorCodeAsync的调用逻辑
如果直接调用_signInManager.SendTwoFactorCodeAsync(model.SelectedProvider)还是报错,就绕过SignInManager,直接用UserManager生成验证码并复用登录时的发送逻辑:
// 生成2FA验证码(替代SignInManager的方法) var token = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); // 调用你登录时用的发送逻辑(比如短信/邮件发送,和SendCode方法里的逻辑一致) await SendVerificationCode(user, model.SelectedProvider, token); // 跳转到2FA验证页面,传递必要参数 return RedirectToAction("VerifyChangePassword2FA", new { provider = model.SelectedProvider, newPassword = model.NewPassword, currentPassword = model.CurrentPassword // 后续修改密码需要 }); }
第三步:2FA验证完成后修改密码
在验证页面的Action里,先校验验证码,再执行密码修改:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> VerifyChangePassword2FA(VerifyChangePassword2FAViewModel model) { if (!ModelState.IsValid) return View(model); var userId = _signInManager.GetUserId(User); var user = await _userManager.FindByIdAsync(userId); if (user == null) return View("Error"); // 验证2FA验证码 var verifyResult = await _userManager.VerifyTwoFactorTokenAsync( user, model.Provider, model.Code ); if (!verifyResult) { ModelState.AddModelError(string.Empty, "验证码无效或已过期"); return View(model); } // 执行密码修改 var passwordResult = await _userManager.ChangePasswordAsync( user, model.CurrentPassword, model.NewPassword ); if (passwordResult.Succeeded) { // 修改成功后重新登录用户 await _signInManager.SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } // 处理密码修改失败的错误 foreach (var error in passwordResult.Errors) ModelState.AddModelError(string.Empty, error.Description); return View(model); }
为什么这个方法能解决问题?
UserManager.GenerateTwoFactorTokenAsync是直接通过用户实体生成验证码,不依赖SignInManager的临时上下文,完全绕开了"找不到用户ID"的问题。同时复用登录时的发送逻辑,保证了2FA流程的一致性。
内容的提问来源于stack exchange,提问作者Shak
相关产品推荐
相关产品推荐

