我想在Asp.Net MVC 5中实现“忘记密码”功能。
请告诉我哪里可以改进。
public ActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(ForgotPasswordMV viewModel)
{
if (ModelState.IsValid)
{
if (SecurityHelper.SendEmail(viewModel.Email))
return RedirectToAction("VerifyToken", new { email = viewModel.Email });
ModelState.AddModelError("Email", "Email not found");
}
return View();
}
public ActionResult VerifyToken(string email = null)
{
VerifyTokenMV viewModel = new VerifyTokenMV
{
Email = email,
Token = ""
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult VerifyToken(VerifyTokenMV viewModel)
{
if (ModelState.IsValid)
{
if (SecurityHelper.VerifyToken(viewModel.Token, viewModel.Email))
return RedirectToAction("ConfirmPassword", new { email = viewModel.Email });
ModelState.AddModelError("Token", "Token does not match");
}
return View();
}
public ActionResult ConfirmPassword(string email = null)
{
ConfirmPasswordMV viewModel = new ConfirmPasswordMV
{
EmailID = email
};
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPassword(ConfirmPasswordMV viewModel)
{
if (ModelState.IsValid)
{
if (viewModel.Password == viewModel.ConfirmPassword)
{
SecurityHelper.UpdatePassword(viewModel.EmailID, viewModel.Password);
TempData["Message"] = "Your password has been updated.";
return RedirectToAction(action, homeController);
}
ModelState.AddModelError("", "Password does not match.");
}
return View();
}public class ConfirmPasswordMV
{
[Display(Name = "Enter your new password"), Required]
public string Password { get; set; }
[Display(Name = "Confirm Password"), Required]
public string ConfirmPassword { get; set; }
public string EmailID { get; set; }
}
public class VerifyTokenMV
{
[Display(Name = "Enter Verification Token sent to your email"), Required]
public string Token { get; set; }
public string Email { get; set; }
}
public class ForgotPasswordMV
{
[Display(Name = "Enter your email:"), Required]
public string Email { get; set; }
}@using (@Html.BeginForm("VerifyToken", "Security"))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(m => m.Email)
@Html.ValidationSummary(true, "Please fix below errors")
<div class="form-group">
@Html.LabelFor(m => m.Token)
@Html.TextBoxFor(m => m.Token, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Token)
</div>
<button class="btn btn-primary">Continue</button>
}@using (@Html.BeginForm("ConfirmPassword", "Security"))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(m => m.EmailID)
@Html.ValidationSummary(true, "Please fix below errors")
<div class="form-group">
@Html.LabelFor(m => m.Password)
@Html.TextBoxFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password)
</div>
<div class="form-group">
@Html.LabelFor(m => m.ConfirmPassword)
@Html.TextBoxFor(m => m.ConfirmPassword, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.ConfirmPassword)
</div>
<button class="btn btn-primary">Save Password</button>
}<p>@ViewBag.ErrorMessage</p>
@using (@Html.BeginForm("ForgotPassword", "Security"))
{
@Html.ValidationSummary(true, "Please fix below error")
@Html.AntiForgeryToken()
<div class="form-group">
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email)
</div>
<button class="btn btn-primary">Continue</button>
}发布于 2017-04-24 09:38:41
不是的。这是不安全的,因为你可以改变任何人的电子邮件地址在最后一步。要做到这一点,我所需要做的就是更改存储在隐藏字段中的emailId (例如,使用我的浏览器工具)。
在最后一步中,您必须验证令牌。你根本不需要第二步。
一旦获得重置令牌,就需要两个请求。通常情况下,您会单击电子邮件中的链接。
获取passwordReset (通过重置令牌)
POST passwordReset (通过重置令牌和新密码)
使用重置令牌查找您要更改密码的电子邮件。标记应该是有时间限制的、长的和随机的,以避免猜测。
发布于 2017-04-26 18:28:17
我有几个建议。
首先,您在控制器中遇到了一些代码重复,需要对模型状态进行重复检查,看看Jimmy的帖子,看看解决这个问题的一种方法。您不必完全遵循他的建议,以下是我在一些项目中使用的变体。
public abstract class DefaultController : Controller
{
internal IActionResult Form<TForm>(TForm form, IFormResultRequest<TForm> request, Func<IActionResult> success, Func<IActionResult> failure)
{
if (!ModelState.IsValid)
return failure();
var formResult = request.Handle(form);
if (!formResult.Success)
{
ModelState.AddModelError("error", formResult.ErrorMessage);
failure();
}
return success();
}然后让您的控制器继承此默认控制器,并执行如下操作:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Validate(ValidateEditModel form, IFormResultRequest<ValidateEditModel> request)
{
return Form(form, request,
success: () => RedirectToAction(...),
failure: () => View(form as ValidateViewModel));
}此操作方法从DI容器中注入IFormResultRequest<ValidateEditModel>。如果不想使用依赖项注入,则仍然应该使用某种类型的控制器基类来减少重复的模型验证检查。
第二,这个控制器非常“肥胖”,这意味着它包含了太多的代码,从而导致它不能很好地扩展,并且使它更难阅读。难以阅读的代码很难调试。另外,您正在验证令牌并在控制器中更改密码,这是业务逻辑,而业务逻辑不应该包含在控制器中。我建议使用某种类型的软件设计模式来分离这个逻辑(例如:依赖注入的CQRS )。
这些建议只适用于后端代码,并取决于应用程序的规模。如果您的应用程序足够小,那么使用依赖注入的CQRS很可能并不理想,因为它所提供的回报将不值得付出努力。无论哪种方式,您的业务逻辑都需要包含在它自己的层中。
https://codereview.stackexchange.com/questions/161566
复制相似问题