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

dev*_*ock 48 asp.net asp.net-identity

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

tra*_*max 48

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");
}
Run Code Online (Sandbox Code Playgroud)

  • 如果用户输入错误/不存在的电子邮件,这种方法会不会造成麻烦?我宁愿将新电子邮件存储在单独的字段中,并且只有在确认完成后才更新"电子邮件". (10认同)
  • 此外,您应该在更新用户时将EmailConfirmed设置为false ... (6认同)
  • 如果新电子邮件地址错误并且用户名/电子邮件设置为该新电子邮件地址,那么用户将无法再登录,也无需单击确认链接.... (5认同)
  • 我建议您也注销用户,以便他们无法继续通过基于cookie的身份验证,直到他们重新确认他们的电子邮件:http://stackoverflow.com/questions/25878218/asp-net-identity-2-0-签出另一个用户 (3认同)

Chr*_*is_ 38

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

}
Run Code Online (Sandbox Code Playgroud)

如果您想要查看更深入的示例,请查看此处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;
Run Code Online (Sandbox Code Playgroud)

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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

跳转到我们的管理控制器中的索引操作,将其添加到我们的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
        };
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)

4.添加这些新修改

首先,让我们添加ChangeEmail:

查看型号:

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

行动:

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

        return View(model);
    }
Run Code Online (Sandbox Code Playgroud)

视图:

@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>
}
Run Code Online (Sandbox Code Playgroud)

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");
    }
Run Code Online (Sandbox Code Playgroud)

现在添加该警告:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

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

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

    }
Run Code Online (Sandbox Code Playgroud)

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);
     }
 }
Run Code Online (Sandbox Code Playgroud)

  • 我们可以添加声明而不是添加其他字段 (4认同)
  • 这是一个很好的完整答案,应该是公认的.感谢发布这个非常有帮助. (2认同)