我已经建立了一个MVC 5网站。添加用户会生成并发送带有确认令牌的电子邮件。当用户单击电子邮件链接时,站点会响应,要求用户设置初始密码。
当用户尝试设置初始密码时,用户尚未登录,因此User.Identity.GetUserId()
为null
。如何将确认链接中的用户ID传递给SetPassword()
的呼叫?
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
return View("Error");
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
using (var s = new UserStore())
{
var u = s.FindByIdAsync(userId).Result;
await s.SetEmailConfirmedAsync(u, true);
return View("ConfirmEmail", new { Id = userId }); // This doesn't seem to make Id available.
}
else
return View("Error");
}
@{
ViewBag.Title = "Confirm Email";
}
<h2>@ViewBag.Title.</h2>
<div>
<p>
Thank you for confirming your email.
Please @Html.ActionLink("click here to create a password.",
"SetPassword", "Manage",
routeValues: null, // I've tried passing new { Id = Model.Id }, but Id isn't available at run-time.
htmlAttributes: new { id = "loginLink" })
</p>
</div>
// POST: /Manage/SetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
答案 0 :(得分:0)
一个简单的解决方法是向SetPassword方法添加一个参数:
public async Task<ActionResult> SetPassword(SetPasswordViewModel model, Guid userIdentifier)
然后,ConfirmEmail.cshtml将更改为:
Please @Html.ActionLink("click here to create a password.",
"SetPassword", "Manage",
routeValues: null, // I've tried passing new { Id = Model.Id }, but Id isn't available at run-time.
htmlAttributes: new { id = "loginLink", userIdentifier = Model.ID })
当用户点击该链接时,您现在应该使用发送电子邮件的用户的ID填写userIdentifier。
它不是超级安全,因为他们可以通过任何Guid(如果他们可以找出要发送的那个)。
您可能希望按ID(而不是userIdentifier)跟踪可以使电子邮件到期的PasswordRequest对象,在这种情况下,他们需要请求新的电子邮件。这样一旦使用了PasswordRequest记录,就可以删除它以防止重用。
注意:PasswordRequest只是一个组成名称。
答案 1 :(得分:0)
您在首次创建用户时收集密码,这样您就不需要UserManager.CreateAsync()
的ID。在您创建新行(并生成ID)后,您可以在注册操作中发送确认电子邮件令牌。
SetPassword()
操作适用于现有且经过身份验证的用户更改其密码。您不应将其标记为[AllowAnonymous]
。
答案 2 :(得分:0)
您可以使用查询字符串,在确认令牌中可以加密用户名或用户ID,当您到达重置页面时对其进行解密并将Identity
设置为任何人。
编辑:除此之外,您还可以在数据库InitialSetup
中创建一个新表,其中包含user
,EncryptedId(guid)
,Initial
列。
当有人点击电子邮件链接时,他们需要输入username
和新密码。那时您将检查输入用户名是否与EncryptedId(guid)
相同。