ASP.NET MVC 5 Identity 2.0设置初始用户密码

时间:2015-06-10 17:03:48

标签: asp.net-mvc

我已经建立了一个MVC 5网站。添加用户会生成并发送带有确认令牌的电子邮件。当用户单击电子邮件链接时,站点会响应,要求用户设置初始密码。

当用户尝试设置初始密码时,用户尚未登录,因此User.Identity.GetUserId()null。如何将确认链接中的用户ID传递给SetPassword()的呼叫?

AccountController.cs

// 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");
}

ConfirmEmail.cshtml

@{
    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>

ManageController.cs

// 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);
}

3 个答案:

答案 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中创建一个新表,其中包含userEncryptedId(guid)Initial列。

当有人点击电子邮件链接时,他们需要输入username和新密码。那时您将检查输入用户名是否与EncryptedId(guid)相同。