.NET身份电子邮件/用户名更改

时间:2014-08-29 14:08:46

标签: asp.net asp.net-identity

有没有人知道如何让用户通过电子邮件确认更改带有ASP.NET身份的用户名/电子邮件?有很多关于如何更改密码的例子,但是我找不到任何相关信息。

5 个答案:

答案 0 :(得分:43)

2017年12月更新评论中提出了一些好处:

  • 如果用户输入了错误的电子邮件,最好在收到确认后为新电子邮件设置单独的字段。等到新电子邮件确认后再将其作为主电子邮件。请参阅下面Chris_的非常详细的答案。
  • 也可能存在使用该电子邮件的帐户存在的情况 - 请确保您也检查,否则可能会出现问题。

这是一个非常基本的解决方案,并未涵盖所有可能的组合,因此请使用您的判断并确保您阅读评论 - 在那里提出了非常好的观点。

// get user object from the storage
var user = await userManager.FindByIdAsync(userId);

// change username and email
user.Username = "NewUsername";
user.Email = "New@email.com";

// Persiste the changes
await userManager.UpdateAsync(user);

// generage email confirmation code
var emailConfirmationCode = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);

// generate url for page where you can confirm the email
var callbackurl= "http://example.com/ConfirmEmail";

// append userId and confirmation code as parameters to the url
callbackurl += String.Format("?userId={0}&code={1}", user.Id, HttpUtility.UrlEncode(emailConfirmationCode));

var htmlContent = String.Format(
        @"Thank you for updating your email. Please confirm the email by clicking this link: 
        <br><a href='{0}'>Confirm new email</a>",
        callbackurl);

// send email to the user with the confirmation link
await userManager.SendEmailAsync(user.Id, subject: "Email confirmation", body: htmlContent);



// then this is the action to confirm the email on the user
// link in the email should be pointing here
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    var confirmResult = await userManager.ConfirmEmailAsync(userId, code);

    return RedirectToAction("Index");
}

答案 1 :(得分:37)

Trailmax得到了大部分权利,但正如评论所指出的那样,如果他们在更新时搞砸了他们的新电子邮件地址,用户将会陷入困境。

要解决此问题,必须向用户类添加其他属性并修改登录。 (注意:这个答案将通过MVC 5项目解决)

这是我拍摄的地方:

<强> 1。修改您的用户对象 首先,让我们更新应用程序用户以添加我们需要的附加字段。您将在Models文件夹的IdentiyModel.cs文件中添加它:

public class ApplicationUser : IdentityUser
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

    [MaxLength(256)]
    public string UnConfirmedEmail { get; set; }//this is what we add

}

如果您想要查看更深入的示例,请在此处查看http://blog.falafel.com/customize-mvc-5-application-users-using-asp-net-identity-2-0/(这是我使用的示例)

此外,它没有在链接文章中提及它,但您也想要更新您的AspNetUsers表:

ALTER TABLE dbo.AspNetUsers
ADD [UnConfirmedEmail] NVARCHAR(256) NULL;

<强> 2。更新您的登录信息

现在我们需要确保我们的登录信息也会检查旧的电子邮件确认信息,以便在我们等待用户确认此新电子邮件时,事情可能会“陷入困境”:

   //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var allowPassOnEmailVerfication = false;
        var user = await UserManager.FindByEmailAsync(model.Email);
        if (user != null)
        {
            if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
            {
                allowPassOnEmailVerfication = true;
            }
        }


        // This now counts login failures towards account lockout
        // To enable password failures to trigger account lockout, I changed to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return allowPassOnEmailVerfication ? RedirectToLocal(returnUrl) : RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

就是这样......你基本上已经完成了!然而,我总是对半个答案感到恼火,这些答案并没有让你过去你后来会遇到的潜在陷阱,所以让我们继续冒险,不管吗?

第3。更新您的管理/索引

在我们的index.cshtml中,让我们为电子邮件添加一个新部分。在我们到达之前,让我们在ManageViewmodel.cs中添加我们需要的字段

public class IndexViewModel
{
    public bool HasPassword { get; set; }
    public IList<UserLoginInfo> Logins { get; set; }
    public string PhoneNumber { get; set; }
    public bool TwoFactor { get; set; }
    public bool BrowserRemembered { get; set; }

    public string ConfirmedEmail { get; set; } //add this
    public string UnConfirmedEmail { get; set; } //and this
}

跳转到我们的管理控制器中的索引操作,将其添加到我们的viewmodel:

        var userId = User.Identity.GetUserId();
        var currentUser = await UserManager.FindByIdAsync(userId);

        var unConfirmedEmail = "";
        if (!String.IsNullOrWhiteSpace(currentUser.UnConfirmedEmail))
        {
            unConfirmedEmail = currentUser.UnConfirmedEmail;
        }
        var model = new IndexViewModel
        {
            HasPassword = HasPassword(),
            PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
            TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
            Logins = await UserManager.GetLoginsAsync(userId),
            BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId),
            ConfirmedEmail = currentUser.Email,
            UnConfirmedEmail = unConfirmedEmail
        };

最后,对于本节,我们可以更新索引,以便我们管理这个新的电子邮件选项:

<dt>Email:</dt>
    <dd>
        @Model.ConfirmedEmail
        @if (!String.IsNullOrWhiteSpace(Model.UnConfirmedEmail))
        {
            <em> - Unconfirmed: @Model.UnConfirmedEmail </em> @Html.ActionLink("Cancel", "CancelUnconfirmedEmail",new {email=Model.ConfirmedEmail})
        }
        else
        {
            @Html.ActionLink("Change Email", "ChangeEmail")
        }
    </dd>

<强> 4。添加这些新修改

首先,让我们添加ChangeEmail:

查看型号:

public class ChangeEmailViewModel
{
    public string ConfirmedEmail { get; set; } 
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    [DataType(DataType.EmailAddress)]
    public string UnConfirmedEmail { get; set; } 
}

采取行动:

 public ActionResult ChangeEmail()
    {
        var user = UserManager.FindById(User.Identity.GetUserId());
        var model = new ChangeEmailViewModel()
        {
            ConfirmedEmail = user.Email
        };

        return View(model);
    }

查看:

@model ProjectName.Models.ChangeEmailViewModel
@{
ViewBag.Title = "Change Email";
}

<h2>@ViewBag.Title.</h2>

@using (Html.BeginForm("ChangeEmail", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <h4>New Email Address:</h4>
    <hr />
    @Html.ValidationSummary("", new { @class = "text-danger" })
    @Html.HiddenFor(m=>m.ConfirmedEmail)
    <div class="form-group">
        @Html.LabelFor(m => m.UnConfirmedEmail, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.UnConfirmedEmail, new { @class = "form-control" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" class="btn btn-default" value="Email Link" />
        </div>
    </div>
}

HttpPost行动:

    [HttpPost]
    public async Task<ActionResult> ChangeEmail(ChangeEmailViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return RedirectToAction("ChangeEmail", "Manage");
        }

        var user = await UserManager.FindByEmailAsync(model.ConfirmedEmail);
        var userId = user.Id;
        if (user != null)
        {
            //doing a quick swap so we can send the appropriate confirmation email
            user.UnConfirmedEmail = user.Email;
            user.Email = model.UnConfirmedEmail;
            user.EmailConfirmed = false;
            var result = await UserManager.UpdateAsync(user);

            if (result.Succeeded)
            {

                string callbackUrl =
                await SendEmailConfirmationTokenAsync(userId, "Confirm your new email");

                var tempUnconfirmed = user.Email;
                user.Email = user.UnConfirmedEmail;
                user.UnConfirmedEmail = tempUnconfirmed;
                result = await UserManager.UpdateAsync(user);

                callbackUrl = await SendEmailConfirmationWarningAsync(userId, "You email has been updated to: "+user.UnConfirmedEmail);


            }
        }
        return RedirectToAction("Index","Manage");
    }

现在添加警告:

    private async Task<string> SendEmailConfirmationWarningAsync(string userID, string subject)
    {
        string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
        var callbackUrl = Url.Action("ConfirmEmail", "Account",
           new { userId = userID, code = code }, protocol: Request.Url.Scheme);
        await UserManager.SendEmailAsync(userID, subject,
           "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

        return callbackUrl;
    }

现在终于,我们可以取消新的电子邮件地址:

    public async Task<ActionResult> CancelUnconfirmedEmail(string emailOrUserId)
    {
        var user = await UserManager.FindByEmailAsync(emailOrUserId);
        if (user == null)
        {
            user = await UserManager.FindByIdAsync(emailOrUserId);
            if (user != null)
            {
                user.UnConfirmedEmail = "";
                user.EmailConfirmed = true;
                var result = await UserManager.UpdateAsync(user);
            }
        }
        else
        {
            user.UnConfirmedEmail = "";
            user.EmailConfirmed = true;
            var result = await UserManager.UpdateAsync(user);
        }
        return RedirectToAction("Index", "Manage");

    }

<强> 5。更新ConfirmEmail(最后一步)

在所有这些来回之后,我们现在可以确认新电子邮件,这意味着我们应该同时删除旧电子邮件。

 var result = UserManager.ConfirmEmail(userId, code);
 if (result.Succeeded)
 {

     var user = UserManager.FindById(userId);
     if (!string.IsNullOrWhiteSpace(user.UnConfirmedEmail))
     {
         user.Email = user.UnConfirmedEmail;
         user.UserName = user.UnConfirmedEmail;
         user.UnConfirmedEmail = "";

         UserManager.Update(user);
     }
 }

答案 2 :(得分:1)

还没有看过ChangeEmailOnIdentity2.0ASPNET,但是您是否不能利用UserName和Email值通常匹配的事实呢?这样,您可以根据要求更改“电子邮件”列,然后根据确认更改“用户名”。

这两个控制器似乎对我有用:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ChangeUserName(LoginViewModel model)
    {
        IdentityResult result = new IdentityResult();
        try
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

                SignInStatus verify = await SignInManager.PasswordSignInAsync(user.UserName, model.Password, false, false);

                if (verify != SignInStatus.Success)
                {
                    ModelState.AddModelError("Password", "Incorrect password.");
                }
                else
                {
                    if (model.Email != user.Email)
                    {
                        user.Email = model.Email;
                        user.EmailConfirmed = false;

                        // Persist the changes
                        result = await UserManager.UpdateAsync(user);

                        if (result.Succeeded)
                        {
                            string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                            var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code }, protocol: Request.Url.Scheme);
                            await UserManager.SendEmailAsync(user.Id, "Confirm your updated email", "Please confirm your email address by clicking <a href=\"" + callbackUrl + "\">this</a>");

                            return RedirectToAction("Index", new { Message = ManageMessageId.ChangeUserNamePending });
                        }
                    }
                    else
                    {
                        ModelState.AddModelError("Email", "Address specified matches current setting.");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            result.Errors.Append(ex.Message);
        }
        AddErrors(result);
        return View(model);
    }

    [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)
        {
            var user = await UserManager.FindByIdAsync(userId);
            if (user.Email != user.UserName)
            {
                // Set the message to the current values before changing
                String message = $"Your email user name has been changed from {user.UserName} to {user.Email} now.";

                user.UserName = user.Email;
                result = await UserManager.UpdateAsync(user);
                if (result.Succeeded)
                {
                    ViewBag.Message = message;

                    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                }
                else
                {
                    result.Errors.Append("Could not modify your user name.");
                    AddErrors(result);

                    return View("Error");
                }
            }
            return View("ConfirmEmail");
        }
        else
        {
            return View("Error");
        }
    }

答案 3 :(得分:0)

我按照乔纳森(Jonathan)的步骤去了一个全新的ASP.NET项目,以测试更改,并且像个魅力一样工作。这是指向repository

的链接

答案 4 :(得分:0)

万一有人在寻找使用Asp.Net Core的解决方案: 这里的事情要简单得多,请参阅SO上的这篇文章 AspNet Core Generate and Change Email Address