【发布时间】:2014-10-23 13:38:55
【问题描述】:
有谁知道如何使用户能够通过电子邮件确认更改具有 ASP.NET 身份的用户名/电子邮件?有很多关于如何更改密码的示例,但我找不到任何内容。
【问题讨论】:
有谁知道如何使用户能够通过电子邮件确认更改具有 ASP.NET 身份的用户名/电子邮件?有很多关于如何更改密码的示例,但我找不到任何内容。
【问题讨论】:
2017 年 12 月更新 在 cmets 中提出了一些优点:
这是一个非常基本的解决方案,并未涵盖所有可能的组合,因此请使用您的判断并确保您通读了 cmets - 那里提出了非常好的观点。
// 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");
}
【讨论】:
Email。
Trailmax 大部分都是正确的,但正如 cmets 指出的那样,如果用户在更新时弄乱了新的电子邮件地址,他们基本上会陷入困境。
要解决这个问题,有必要向您的用户类添加其他属性并修改登录。 (注意:这个答案将通过 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
}
在我们的 Manage 控制器中跳转到 index 操作,将其添加到我们的视图模型中:
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);
}
}
【讨论】:
我按照 Jonathan 的步骤创建了一个全新的 ASP.NET 项目以测试更改,并且工作起来非常有魅力。这是repository的链接
【讨论】:
尚未查看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");
}
}
【讨论】:
如果有人正在寻找使用 Asp.Net Core 的解决方案: 这里的事情要简单得多,请参阅 SO 上的这篇文章 AspNet Core Generate and Change Email Address
【讨论】: