INTRODUCCIÓN:
Hola, bienvenido al cuarto capítulo del tutorial “Login Completo” en C#, Visual Basic, SQL Server, WinForms, Programación Orientada a Objetos (POO) y Arquitectura en Capas, con buenas prácticas y estrategias (Nivel intermedio).
En este tutorial aprenderá: como el usuario pueda recuperar su contraseña mediante su correo electrónico. Puede ver este tutorial en YouTube.
En los capítulos anteriores, se diseñó el formulario login plano modernista, validar el usuario y contraseña del usuario al iniciar sesión, mostrar los datos del usuario en cualquier formulario, cerrar sesión, y aplicar seguridad y privilegios de usuario (Te recomiendo verlo, ya que en este tutorial estará basado sobre ello).
Puede ver todos los capítulos en los siguientes enlaces:
PRE-TUTORIAL:
Antes de empezar con el tutorial, necesitamos saber conceptos clave sobre, sistemas de gestión de E-mails, servicios de correo electrónico, conjunto de protocolos TCP/IP, principalmente cómo funcionan e interactúan.
Protocolos de correo electrónico: SMTP, POP3 e IMAP
Podemos enviar y recibir correos electrónicos gracias a los protocolos SMTP, POP3 e IMAP, estos protocolos pertenecen al gran conjunto de los protocolos TCP/IP. Así que, si tienes planeado poner un servidor de email o piensas configurar el cliente de email en la aplicación que desarrollas (como en este caso), tienes que saber el uso específico de cada uno de estos protocolos y las reglas de comunicación entre los dispositivos.
Bueno en pre-resumen, los sistemas que gestionan correos electrónicos, utilizan tres protocolos principales:
- SMTP, su función específica es de enviar el correo (correo saliente).
- POP3/IMAP, para recibir el correo (correo entrante).
SMTP (Simple Mail Transfer Protocol)
El protocolo de transferencia de mensajes simple es muy usado para enviar correos desde un cliente de correo, a un servidor de correo electrónico, ya sea gmail, outlook, yahoo, apple mail o correo corporativo (mail@midominio.com). También se usa con bastante frecuencia para reenviar o retransmitir correos de un servidor de correo electrónico a otro.
En tal sentido, es el protocolo que nos interesa, ya que enviaremos correos electrónicos desde nuestra aplicación, en este caso, enviar un código o la contraseña al email del usuario cuando este solicita.
Importante
Para poder enviar correos, es obvio que se necesita una dirección de correo electrónico, no se puede enviar un correo anónimamente, es como intentar hacer una llamada sin número (imposible).
Por tal razón; para este tutorial creé una dirección de correo electrónico en GMAIL, soporteSystemMiTienda@gmail.com, esta dirección de correo se encargará de enviar los mensajes del sistema (mensajes de error, reportes, notificaciones, solicitudes de usuario), a cualquier proveedor, ya sea gmail, outlook, yahoo, apple mail o un correo corporativo (OJO).
Configurar Cliente SMTP
En conclusión; para poder enviar emails desde nuestra aplicación, necesitamos crear y configurar un cliente SMTP, para ello, es necesario saber y tener 4 cosas principales.
- Credenciales.- Conformada por la dirección de correo electrónico remitente y su contraseña (Credentials):
Dirección de correo electrónico=soporteSystemMiTienda@gmail.com
Contraseña=admin4321 - Servidor de correo saliente: Nombre del servidor, será similar al proveedor o dominio al que pertenece el correo remitente. En este caso gmail.
Host= smtp.gmail.com - Puerto: Puerto del servidor saliente de gmail (SMTP)
Port= 587 - Cifrado SSL: Es opcional, pero importante, saber si su cliente smtp admite la encriptación SSL o TLS. En este caso GMAIL, envía los mensajes encriptados.
SSL= True
^Estos datos serán colocados en el código de nuestra aplicación.
Si usas otro proveedor como Outlook, Yahoo o tu dominio (mail@tudominio.com), tienes que averiguar estos datos actualizados (algunos cambian de host o puerto al pasar los años).
Base de Datos
En el capítulo 2, se creó la base de datos mi compañía, una tabla usuarios, algunos campos, y tres registros de usuarios ficticios. Para este tutorial, el único campo que nos interesa, es la dirección de correo electrónico del usuario. Ademas, es esencial que los usuarios deben tener su email real registrado en la base de datos, ya que es uno de los medios más seguros para recuperar una contraseña.
TUTORIAL:
Paso 1: Codificar- Capa de Acceso a Datos
Como se mencionó en el pequeño curso de patrones de software, en el tema de arquitectura en capas; la capa de acceso a datos o persistencia, es responsable de la comunicación con la base de datos (MsSql, MySql, Oracle), servicios de correo electrónico y servicios externos, sin embargo, también recordemos que el modelo actual en la que trabajamos, es la arquitectura tradicional en capas, actualmente, en la arquitectura en capas, los servicios de correo electrónico es responsabilidad de la capa de infraestructura.
Bien, dicho lo anterior, iniciemos con organizar los componentes de la capa de acceso a datos.
- Creamos una carpeta (sub capa) para la comunicación con la base de datos SQL server (SQLServer) e introducimos dentro, la clase conexión y los objetos de acceso a datos (DAO).
- Creamos otra carpeta para los servicios de correo electrónico(MailServices).
-Sub-Capa Servicios de correo electrónico
- En la carpeta Mail Services, agregamos una nueva clase pública abstracta (Clase base), de nombre pondré servidor de correo maestro (masterMailServer.cs), esta clase se encargará de enviar cualquier correo electrónico, a uno o varios destinatarios. Esta clase será heredada desde otras clases, es decir, podrá ser usada por cualquier clase de la subcapa servicios de correo (imagina que es igual a la clase conexión a SQL Server).
- Una vez creado la clase anterior, importamos las bibliotecas net.mail y net.
using System.Net.Mail; using System.Net;
Imports System.Net.Mail Imports System.Net
- Declaramos los atributos necesarios y un método para inicializar el cliente smtp.
- También creamos un método para enviar los mensajes de correo, con parámetros de entrada para: el asunto, cuerpo del mensaje, y correo del destinatario (a quien, o a quienes será enviando el mensaje).
IMPORTANTE: El parámetro Correo del destinatario (recipientMail), debe ser de tipo lista, ya que en muchas ocasiones es necesario enviar correos por lote, por ejemplo; enviar promociones a varios clientes, obvio no enviarás la promoción uno por uno a cada cliente.
MasterMailServer.cs /.vb
public abstract class MasterMailServer { //Atributos private SmtpClient smtpClient; protected string senderMail { get; set; } protected string password { get; set; } protected string host { get; set; } protected int port { get; set; } protected bool ssl { get; set; } //Inicializar propiedades del cliente SMTP protected void initializeSmtpClient() { smtpClient = new SmtpClient(); smtpClient.Credentials = new NetworkCredential(senderMail,password); smtpClient.Host = host; smtpClient.Port = port; smtpClient.EnableSsl = ssl; } public void sendMail(string subject,string body, List<string> recipientMail) { var mailMessage = new MailMessage(); try { mailMessage.From = new MailAddress(senderMail); foreach (string mail in recipientMail) { mailMessage.To.Add(mail); } mailMessage.Subject = subject; mailMessage.Body = body; mailMessage.Priority = MailPriority.Normal; smtpClient.Send(mailMessage);//Enviar mensaje } catch (Exception ex) { } finally { mailMessage.Dispose(); smtpClient.Dispose(); } } }
Public MustInherit Class MasterEmailServer '//Atributos' Private smtpClient As SmtpClient Protected senderMail As String Protected password As String Protected host As String Protected port As Integer Protected ssl As Boolean Protected Sub initializeSmptClient() smtpClient = New SmtpClient smtpClient.Credentials = New NetworkCredential(senderMail, password) smtpClient.Host = host smtpClient.Port = port smtpClient.EnableSsl = ssl End Sub '//Método para enviar los mensajes de correo electrónico a uno o varios destinatarios' Public Sub sendMail(ByVal subject As String, ByVal body As String, ByVal receiverMail As List(Of String)) Dim mailMessage As MailMessage = New MailMessage Try mailMessage.From = New MailAddress(senderMail) For Each mail As String In receiverMail mailMessage.To.Add(mail) Next mailMessage.Subject = subject mailMessage.Body = body mailMessage.Priority = MailPriority.Normal smtpClient.Send(mailMessage) Catch ex As Exception Finally mailMessage.Dispose() smtpClient.Dispose() End Try End Sub
El propósito de una clase Abstracta (también conocida como una clase base ) es definir la funcionalidad que es común a todas las clases que deriven de ella. Esto evita que las clases derivadas tengan que redefinir los elementos comunes. En algunos casos, esta funcionalidad común no es lo suficientemente completa para hacer un objeto utilizable, y cada clase derivada define la funcionalidad que falta. En tal caso, desea que el código consumidor cree objetos solo a partir de las clases derivadas. Usted utiliza
Fuente Microsoft- Importante.. Tener muy en cuentaAbstract
en la clase base para hacer cumplir esto.
- En la carpeta servicios mail, agregamos otra clase para enviar los correos electrónicos solo del soporte del sistema o aplicación (systemSupportMail.cs), para mensajes de error, reportes, notificaciones y solicitudes de usuario (como en este caso). En esta carpeta puedes agregar otras clases, como, servicios mail para el área de ventas, gerencia, contabilidad, usuarios en general.
- Una vez creado la clase systemSupportMail, indicamos que hereda de la clase masterMailServer e inicializamos los valores del cliente smtp en el constructor de la clase. Sin embargo, estos datos se deben obtener desde la base de datos, ya que la contraseña o dirección de correo electrónico pueden cambiar en algún momento, y eso resulta perjudicial (Si se trata de un sistema para una entidad real).
class SystemSupportMail:MasterMailServer { public SystemSupportMail() { senderMail = "soporteSystemTiendita@gmail.com"; password = "admin4321"; host = "smtp.gmail.com"; port = 587; ssl = true; initializeSmtpClient(); } }
Public Class SystemSupportMail Inherits MasterEmailServer Public Sub New() senderMail = "soporteSystemTiendita@gmail.com" password = "admin4321" host = "smtp.gmail.com" port = 587 ssl = True initializeSmtpClient() End Sub End Class
RECUERDEN: Cualquier clase que hereda de una clase base, puede usar todos sus atributos y métodos públicos y protegidos, más no privados (Solo puede ser accedida por la propia clase). Para mas información puedes investigar los pilares de la herencia y encapsulación de Programación Orientada a Objetos (POO).
-Sub-Capa Comunicación con base de datos sql
- En la clase objeto de acceso a datos de usuario (UserDao.cs / .vb), creamos un método para solicitar la contraseña del usuario a la base de datos mediante el nombre de usuario de inicio de sesión o su dirección de correo electrónico. Obtenemos los datos e instanciamos la clase SystemSupportMail.cs / .vb, para enviar la contraseña del usuario a la dirección de correo electrónico del usuario. El método debe tener tipo de retorno STRING, para poder mostrar un mensaje de respuesta al usuario en la interfaz gráfica.
UserDao.cs / .vb
public class UserDao : ConnectionToSql { public string recoverPassword(string userRequesting) { using (var connection = GetConnection()) { connection.Open(); using (var command = new SqlCommand()) { command.Connection = connection; command.CommandText = "select *from Users where LoginName=@user or Email=@mail"; command.Parameters.AddWithValue("@user", userRequesting); command.Parameters.AddWithValue("@mail", userRequesting); command.CommandType = CommandType.Text; SqlDataReader reader = command.ExecuteReader(); if (reader.Read() == true) { string userName = reader.GetString(3) + ", " + reader.GetString(4); string userMail = reader.GetString(6); string accountPassword = reader.GetString(2); var mailService = new MailServices.SystemSupportMail(); mailService.sendMail( subject: "SYSTEM: Password recovery request", body: "Hi, " + userName + "\nYou Requested to Recover your password.\n" + "your current password is: " + accountPassword + "\nHowever, we ask that you change your password inmediately once you enter the system.", recipientMail: new List<string> { userMail } ); return "Hi, " + userName + "\nYou Requested to Recover your password.\n" + "Please check your mail: " + userMail + "\nHowever, we ask that you change your password inmediately once you enter the system."; } else return "Sorry, you do not have an account with that mail or username"; } } } }
Public Class UserDao Inherits ConnectionToSql Public Function requestUserPassword(ByVal requestingUser As String) As String Using connection = GetConnection() connection.Open() Using command = New SqlCommand() command.Connection = connection command.CommandText = "select *from Users where LoginName=@user or Email=@mail" command.Parameters.AddWithValue("@user", requestingUser) command.Parameters.AddWithValue("@mail", requestingUser) command.CommandType = CommandType.Text Dim reader As SqlDataReader = command.ExecuteReader() If reader.Read() = True Then Dim userName As String = reader.GetString(3) & ", " + reader.GetString(4) Dim userMail As String = reader.GetString(6) Dim accountPassword As String = reader.GetString(2) Dim systemSupport = New SystemSupportMail() systemSupport.sendMail( subject:="SYSTEM: Password recovery request", body:="Hi " & userName & vbLf & "you requested to recover your password." & vbLf & "Your current password is: " & accountPassword & vbLf & "However, we ask that you change your password immediately once you enter the system", receiverMail:=New List(Of String) From {userMail} ) Return "Hi " & userName & vbLf & "you requested to recover your password." & vbLf & "Please check your email: " & userMail & vbLf & "However, we ask that you change your password immediately once you enter the system" Else Return "Sorry, you do not have an account with that email or username." End If End Using End Using End Function End Class
Ya comenté en algunos videos la importancia de usar USING, La declaración using garantiza que se llame a Dispose una vez termine de ejecutarse los códigos dentro del bloque using, incluso si ocurre una excepción. Para entender mejor, una vez que termine de ejecutarse el método recoverPassword, se desechará los objetos sqlConnection y sqlCommand, es por ello que no es necesario, cerrar la conexión o limpiar los parámetros de sqlCommand.
Importante, Tener muy en cuenta
Paso 2: Codificar- Capa de Dominio / Negocio
- En la clase modelo de usuario (UserModel.cs), creamos un método de nombre recuperar contraseña e invocamos y retornamos el resultado del método recoverPassword() de la capa de acceso a datos (UserDao.cs).
public class UserModel { UserDao userDao = new UserDao(); public string recoverPassword(string userRequesting) { return userDao.recoverPassword(userRequesting); } }
Imports DataAccess Public Class UserModel Dim userDao As New UserDao() Public Function recoverPassword(requestingUser As String) As String Return userDao.requestUserPassword(requestingUser) End Function End Class
Paso 3: Codificar- Capa de Presentación
- Agregamos un nuevo formulario para recuperar la contraseña del usuario (FormRecoverPassword.cs) y diseñamos de la siguiente manera:
- En el botón enviar (Send), invocamos el método anterior creado en la capa de dominio (UserModel.cs /.vb), y mostrarnos el resultado en un label. Para ello necesitamos importar la capa de domino e instanciar a la clase Modelo Usuario.
using Domain; namespace Presentation { public partial class FormRecoverPassword : Form { private void btnSend_Click(object sender, EventArgs e) { var user = new UserModel(); var result = user.recoverPassword(txtUserRequest.Text); lblResult.Text = result; } } }
Imports Domain Public Class FormRecoverPassword Private Sub btnSend_Click(sender As Object, e As EventArgs) Handles btnSend.Click Dim userModel As New UserModel() Dim result = userModel.recoverPassword(txtRequestingUser.Text) lblResultMessage.Text = result End Sub End Class
- Desde el botón, link o label de ¿Has olvidado contraseña? Del formulario login, instanciamos al formulario anterior.
public partial class FormLogin : Form { private void linkpass_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { var recoverPassword = new FormRecoverPassword(); recoverPassword.ShowDialog(); } }
Imports Domain Public Class FormLogin Private Sub lblrecoverPassword_Click(sender As Object, e As EventArgs) Handles lblrecoverPassword.Click Dim frmRecoverPassword As New FormRecoverPassword() frmRecoverPassword.ShowDialog() End Sub End Class
Paso Final: Probar la aplicación
- Para probar la aplicación, es necesario que haya usuarios registrados con emails reales, por ello, en la base de datos, me registraré como nuevo usuario e introduciré mi dirección de correo electrónico real, de Outlook, como dije, nuestra aplicación puede enviar correos a cualquier proveedor.
insert into Users values ('RJCodeDev','rjco1998','Ronet John','Caceres','Accounting','RJCodeAdvance@outlook.com')
LoginName= RJCodeDev //Mi nombre de usuario para inicio de sesión en la app
Password=rjco1998 //Mi Contraseña para ingresar a la aplicación
Email= RJCodeAdvance@outlook.com //Mi dirección de correo electrónico
- Solicitaré recuperar mi contraseña por medio de mi nombre de usuario de inicio de sesión (LoginName).
- Como notéis, la aplicación me informó lo siguiente:
«Hola Ronet John, Caceres.
Solicitó recuperar su contraseña.
Por favor revise su correo electrónico: RJCodeAdvance@outlook.com
Sin embargo, le pedimos que cambie su contraseña inmediatamente una vez que ingrese al sistema» - Si reviso mi correo electrónico, definitivamente el correo está en mi bandeja de entrada.
- El mensaje fue enviado desde soporteSystemTiendita@gmail.com , el asunto del mensaje: Sistema: Solicitud de recuperación de contraseña, en el cuerpo del mensaje, esta mi nombre completo y mi contraseña que olvidé, ahora si podré ingresar a la aplicación :V
Eso es todo 🙂
Puede ver este tutorial en YouTube
C#
VB
DESCARGAS:
Descargar Base de Datos
Descargar Proyecto Login Completo + CRUD
La descarga del proyecto (No es gratis) incluye las funciones realizadas en los 6 capítulos del proyecto LOGIN COMPLETO EN C# o LOGIN COMPLETO EN VB.NET, sin embargo, el proyecto tiene un código fuente totalmente modernizado y optimizado, además de incluir CRUD completo con manejo de imágenes, validación de datos duplicados, entre otros. Todo ello con SQL Server, WinForm, Programación Orientada a Objetos (POO) y Arquitectura en Capas, con buenas prácticas y estrategias, tanto de nivel intermedio o avanzado. Puedes descargar las versiones de demostración y obtener los proyectos desde lo botones a continuación:
C# (C-Sharp)
VB.NET (Visual Basic)
Los comentarios están cerrados.