Funcionalidad "Olvidé mi contraseña" -- # campo con asp.net campo con asp.net-mvc campo con asp.net-mvc-5 campo con razor camp codereview Relacionados El problema

“Forgot password” functionality


6
vote

problema

Español

Quiero implementar la funcionalidad "Contraseña olvidada" en ASP.NET MVC 5. Aquí está el flujo de código:

  1. Tomar el usuario de correo electrónico ID
  2. Deje que el usuario ingrese al token recibido en su correo electrónico de correo electrónico
  3. Si el token coincide, redirige la página de restablecimiento de contraseña
  4. establece la nueva contraseña

Por favor dime dónde puedo mejorar.

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

Ver modelo

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

vistas

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

Ver 2

  int[] new = {0, 2, 99, 111, 356, 888, 896, 3454, 5434, 7324, 78365}; 0  

Ver 3

  int[] new = {0, 2, 99, 111, 356, 888, 896, 3454, 5434, 7324, 78365}; 1  

nota

  1. Tenga en cuenta que estoy pasando un correo electrónico a cada pantalla como campo oculto y, por lo tanto, en el controlador, para que pueda verificar el usuario correcto. He pasado usando un objeto anónimo. Es mi intento de novatos, así que por favor, dime las fallas / mejoras que puedo hacer.
  2. ¿Puedo tener solo una vista y hacer todas estas como visión parcial?
  3. ¿Realmente necesito tres modelos de vista separados para estas páginas?
  4. No tengo interacción directa con los modelos, ya que el código Hits API escrito en el código VB.NET.

editar

  1. Tengo tres columnas en db a. token b. correo electrónico c. Marca de tiempo

  2. Expire token si el tiempo es más de 5 minutos.

Original en ingles

I want to implement the "forgot password" functionality in Asp.Net MVC 5. Here is the code flow:

  1. Take user email ID
  2. Let the user enter the token received in his email inbox
  3. If the token matches then redirect for password reset page
  4. Set the new password

Please tell me where I can improve.

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

View Model

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

Views

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

View 2

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

View 3

<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> } 

Note

  1. Kindly note that I am passing email to every single screen as hidden field and thus in controller too so that I can verify the correct user. I have passed in using an anonymous object. It's my novice attempt, so please tell me the flaws/improvements I can make.
  2. Can I have just one view and make all of these as partial view?
  3. Do I really need three separate view models for these pages?
  4. I do not have direct interaction with models as code hits API written in VB.NET code.

EDIT

  1. I have three columns in DB a. token b. emailId c. TimeStamp

  2. I expire token if time is more than 5 minutes.

              
       
       

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

es esto seguro?

no . No es seguro porque puede cambiar la dirección de correo electrónico de cualquiera en el último paso. Para hacerlo, todo lo que necesito hacer es cambiar el emailId almacenado en el campo oculto (por ejemplo, usando mis herramientas de navegador).

En el paso final, usted debe verifique el token. No necesitas el paso 2 en absoluto.

Hay dos solicitudes que necesita una vez que tenga su token de reinicio. Normalmente, hará clic en un enlace en el correo electrónico.

Obtener contraseñaReset (pasar en el token de reinicio)
Publicar PINSPINGRESET (Pase en el token de reinicio y la nueva contraseña)

Use el token de reinicio para buscar el correo electrónico que está cambiando la contraseña. El token debe ser el tiempo limitado, largo y aleatorio para evitar adivinar.

 

Is this secure?

No. It isn't secure because you can change anyone's email address on the last step. To do so, all I need to do is change the emailId stored in the hidden field (for example, using my browser tools).

In the final step you must verify the token. You don't need step 2 at all.

There are two requests you need once you have your reset token. Typically you'd click a link in the email.

GET passwordReset (pass in the reset token)
POST passwordReset (pass in the reset token and the new password)

Use the reset token to look up the email that you're changing the password for. The token should be time limited, long and random to avoid guessing.

 
 
         
         
1
 
vote

Tengo algunas sugerencias.

Primero, está sufriendo de alguna duplicación de código en el controlador con los controles repetitivos del estado del modelo, eche un vistazo a Jimmy Bogard's POST de una forma de resolver este problema. No tiene que seguir exactamente sus recomendaciones, lo siguiente es una variante que utilizo en algunos de mis proyectos.

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

Luego, haga que su controlador hereda de este controlador predeterminado y haga algo así como:

  [HttpPost] [ValidateAntiForgeryToken] public IActionResult Validate(ValidateEditModel form, IFormResultRequest<ValidateEditModel> request) {   return Form(form, request,     success: () => RedirectToAction(...),     failure: () => View(form as ValidateViewModel)); }   

Este método de acción tiene el IFormResultRequest<ValidateEditModel> inyectado desde el contenedor de DI. Si no desea usar la inyección de dependencia, debe usar algún tipo de clase base de controlador para reducir los controles de validación del modelo repetitivo.

Segundo, este controlador es bastante 'obeso', lo que significa que contiene demasiado código, lo que hace que no sea muy extensible, y hace que sea más difícil leer. El código que es más difícil de leer es más difícil de depurar. Además, está verificando el token y está cambiando la contraseña desde su controlador, esta es la lógica de negocios y la lógica de negocios no debe estar contenida dentro del controlador. Sugiero usar algún tipo de patrón de diseño de software para separar esta lógica (por ejemplo: CQRS con inyección de dependencia).

Estas recomendaciones solo se aplican a su código de backend y están contingentes en la escala de su solicitud. Si su aplicación es lo suficientemente pequeña, las cqrs con inyección de dependencia probablemente no serían ideales porque la recompensa lo haría proporcionar no valdría la pena el esfuerzo. De cualquier manera, su lógica empresarial debe estar contenida dentro de su propia capa.

 

I have a few suggestions.

First, you are suffering from some code duplication in the controller with the repetitive checks to the model state, take a look at Jimmy Bogard's post for one way to resolve this problem. You don't have to follow his recommendations exactly, the following is a variant I use in some of my projects.

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

Then have your controller inherit from this default controller and do something like:

[HttpPost] [ValidateAntiForgeryToken] public IActionResult Validate(ValidateEditModel form, IFormResultRequest<ValidateEditModel> request) {   return Form(form, request,     success: () => RedirectToAction(...),     failure: () => View(form as ValidateViewModel)); } 

This action method has the IFormResultRequest<ValidateEditModel> injected from the DI container. If you do not want to use dependency injection you should still use some type of controller base class to reduce the repetitive model validation checks.

Second, this controller is pretty 'obese', meaning it contains too much code, thus causing it not to be very extensible, and it makes it harder to read. Code that is harder to read is harder to debug. Additionally, you are verifying the token and changing the password from within your controller, this is business logic and business logic should not be contained inside the controller. I suggest using some type of software design pattern to separate this logic out (eg: CQRS with dependency injection).

These recommendations only apply to your backend code and are contingent on the scale of your application. If your application is small enough, CQRS with dependency injection would most likely not be ideal because the payoff it would provide wouldn't be worth the effort. Either way, your business logic needs to be contained within its own layer.

 
 
 
 

Relacionados problema

4  Listado de usuarios en Azure Active Directory  ( Listing users in azure active directory ) 
El código a continuación es la acción del índice de un usuario. Básicamente, este código devuelve una lista de usuarios desde Azure Active Directory con Azure...

3  Muestra un abandono basado en otro cambio desplegable  ( Display a dropdown based on another dropdown change ) 
Tengo un desplegable y cuando cambia ese desplegable, quiero mostrar / hacer otro desplegable. ¿Puede alguien ayudarme a mejorarlo? Por lo tanto, una person...

1  Vista parcial para listas desplegables de uso frecuente  ( Partial view for frequently used drop down lists ) 
Tengo una entrada que se necesita en muchas formas, así que creé una vista parcial para eso. En la vista parcial, consulta la base de datos y generaré una lis...

6  Mono alrededor con mono encuesta y ASP.NET MVC  ( Monkeying around with survey monkey and asp net mvc ) 
Intro He sido un desarrollo de escritorio durante mucho tiempo, y nunca he tenido que Mono con el desarrollo web hasta hace muy poco. Tengo la necesidad de ...

10  Autorización basada en características  ( Feature based authorization ) 
autorización basada en características Me parece que si confía en los roles para autorizar una aplicación web, hace que sea muy difícil representar la UI ...

5  Un conjunto de asistentes HTML de Razor para elementos de deshabilitación  ( A set of razor html helpers for disabling elements ) 
Estoy desarrollando una gran forma con muchos elementos. El propósito de esta forma está editando una entidad de dominio. Ahora, terminé de desarrollar toda l...

-1  ¿Debería declarar variables para hacer que los nombres de propiedades del modelo sean más cortos? [cerrado]  ( Should you declare variables to make model property names shorter ) 
cerrado . Esta pregunta necesita detalles o claridad . Actualmente no está aceptando respuestas. ...

5  Redirigiendo a un usuario  ( Redirecting a user ) 
Cuando el usuario hace clic en el botón Primer botón (botón 'Volver a la lista'), redirí al usuario a la página de lista de empleados. ¿Puede por favor revisa...

17  Módulo PowerShell para formatear objetos usando Razor  ( Powershell module for formatting objects using razor ) 
Me las arreglé para correr y correr, pero no tengo idea de si hay mejores formas de hacer esto. He pasado toda la mañana tratando de averiguar la mejor manera...

4  Entrega la opción "Otros" en la desplegable en ASP.NET MVC 5  ( Handing others option in dropdown in asp net mvc 5 ) 
lógica de negocios Tengo un formulario de referencia de la empresa, y en ese formulario tengo desplegable para seleccionar de una lista de la empresa. Per...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos