Registro y Login de usuarios ASP .Net Core
En este post voy a explicar como crear un registro y login de usuarios en un proyecto ASP.NET Core.
Antes de crearnos el proyecto primero tenemos que crear nuestra base de datos en la que los usuarios harán el registro y el login. El script de la tabla usuarios es el siguiente:
CREATE TABLE USUARIOS (
ID_USUARIO INT NOT NULL PRIMARY KEY ,
EMAIL NVARCHAR(150) NOT NULL,
PASS VARBINARY(MAX) NOT NULL,
SALT NVARCHAR(50) NOT NULL,
NOMBRE NVARCHAR(50) NOT NULL,
APELLIDOS NVARCHAR(50) DEFAULT NULL,
TIPO NVARCHAR(15) CHECK( tipo IN('USUARIO','ADMIN') ) DEFAULT 'USUARIO'
)
Ahora pasamos a crearnos un nuevo proyecto en Visual Studio del tipo:
En nuestro proyecto tenemos que tener instalados los siguientes nuget:
Una vez agregados los nuget debemos de crearnos un model para poder mapear nuestra tabla de usuarios:
[Table("USUARIOS")]
public class Usuario
{
[Key]
[Column("ID_USUARIO")]
public int IdUsuario { get; set; }
[Column("PASS")]
public byte[] Password { get; set; }
[Column("EMAIL")]
public string Email { get; set; }
[Column("SALT")]
public string Salt { get; set; }
[Column("NOMBRE")]
public string Nombre { get; set; }
[Column("APELLIDOS")]
public string Apellidos { get; set; }
[Column("TIPO")]
public string Tipo { get; set; }
}
Con el modelo creado podemos podemos crear nuestro repositorio, pero antes debemos de crearnos unas clases que nos van a ayudar a cifrar la contraseña en la base de datos (nunca se deben de guardar sin cifrar). La clase se llamará HelperCryptography, generaremos un salt y cifraremos la contraseña.
public static string GenerateSalt()
{
Random random = new Random();
string salt = "";
for (int i = 1; i <= 50; i++)
{
int numero = random.Next(0, 255);
char letra = Convert.ToChar(numero);
salt += letra;
}
return salt;
}
public static bool compareArrays(byte[] a, byte[] b)
{
bool iguales = true;
if (a.Length != b.Length)
{
iguales = false;
}
else
{
//comparamos byte a byte
for (int i = 0; i < a.Length; i++)
{
if (a[i].Equals(b[i]) == false)
{
iguales = false;
break;
}
}
}
return iguales;
}
public static byte[] EncriptarPassword(string password, string salt)
{
string contenido = password + salt;
SHA256Managed sha = new SHA256Managed();
byte[] salida = Encoding.UTF8.GetBytes(contenido);
for (int i = 1; i <= 107; i++)
{
salida = sha.ComputeHash(salida);
}
sha.Clear();
return salida;
}
A continuación creamos un nuevo controlador llamado MaganeController que será el encargado de hacer el login a los usuarios. Cuando el usuario haga login en la web se crearan unos Claims para poder acceder a las paginas restringidas. Depende del tipo de usuario (usuario normal o administrador) podrá acceder a las paginas que especifiquemos en el HomeController
public IActionResult LogIn()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(string email, string password)
{
Usuario usuario = this.repo.LogInUsuario(email, password);
if (usuario == null)
{
ViewData["MENSAJE"] = "No tienes credenciales correctas";
return View();
}
else
{
//DEBEMOS CREAR UNA IDENTIDAD (name y role)
//Y UN PRINCIPAL
//DICHA IDENTIDAD DEBEMOS COMBINARLA CON LA COOKIE DE
//AUTENTIFICACION
ClaimsIdentity identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
//TODO USUARIO PUEDE CONTENER UNA SERIE DE CARACTERISTICAS
//LLAMADA CLAIMS. DICHAS CARACTERISTICAS PODEMOS ALMACENARLAS
//DENTRO DE USER PARA UTILIZARLAS A LO LARGO DE LA APP
Claim claimUserName = new Claim(ClaimTypes.Name, usuario.Nombre);
Claim claimRole = new Claim(ClaimTypes.Role, usuario.Tipo);
Claim claimIdUsuario = new Claim("IdUsuario", usuario.IdUsuario.ToString());
Claim claimEmail = new Claim("EmailUsuario", usuario.Email);
identity.AddClaim(claimUserName);
identity.AddClaim(claimRole);
identity.AddClaim(claimIdUsuario);
identity.AddClaim(claimEmail);
ClaimsPrincipal userPrincipal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, userPrincipal, new AuthenticationProperties
{
ExpiresUtc = DateTime.Now.AddMinutes(45)
});
return RedirectToAction("Index", "Home");
}
}
public IActionResult ErrorAcceso()
{
ViewData["MENSAJE"] = "Error de acceso";
return View();
}
public async Task<IActionResult> LogOut()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Index", "Home");
}
}
En el HomeController tenemos 4 vistas:
- Index
- Registro: Recibirá los datos del formulario y agregara un nuevo usuario a la base de datos
- PaginaProtegida: En esta pagina solo podrán acceder los usuarios registrados
- AdminUsuarios: En esta pagina solo podrán acceder los usuarios registrados del tipo administrador.
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Registro(string email, string password, string nombre, string apellidos, string tipo)
{
bool registrado = this.repo.RegistrarUsuario(email, password, nombre, apellidos, tipo);
if (registrado)
{
ViewData["MENSAJE"] = "Usuario registrado con exito";
}
else
{
ViewData["MENSAJE"] = "Error al registrar el usuario";
}
return View();
}
[AuthorizeUsers]
public IActionResult PaginaProtegida()
{
return View();
}
[AuthorizeUsers(Policy = "ADMINISTRADORES")]
public IActionResult AdminUsuarios()
{
List<Usuario> usuarios = this.repo.GetUsuarios();
return View(usuarios);
}
Por ultimo en nuestro startup agregamos las dependencias a nuestro proyecto
public void ConfigureServices(IServiceCollection services)
{
string cadena = this.Configuration.GetConnectionString("cadena");
services.AddTransient<RepositoryWeb>();
services.AddDbContext<WebContext>(options => options.UseSqlServer(cadena));
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(50);
});
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, config =>
{
config.AccessDeniedPath = "/Manage/ErrorAcceso";
});
//Necesitamos indicar un provedor de almacenamiento para tempdata
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
services.AddControllersWithViews(options => options.EnableEndpointRouting = false).AddSessionStateTempDataProvider();
services.AddAuthorization(options =>
{
options.AddPolicy("ADMINISTRADORES", policy => policy.RequireRole("ADMIN"));
});
}
Con estos pasos ya tendríamos una registro y un login en nuestra web con paginas protegidas, para poder ver el código completo puedes descargarte el proyecto desde GitHub.
Autor/a: Luis Enrique Frías Araujo
Curso: Desarrollo Web Full Stack, MultiCloud y Multiplataforma
Centro: Tajamar
Año académico: 2021-2022
Enlace a GitHub: https://github.com/luisnrq/RegistroLogin
Estoy adaptando tu ejemplo a mi código, en tu controlador MaganeController, en la linea 59 haces referencia a «userPrincipal», pero a mi me marca el error «userPrincipal is not null here», como si no detectara la variable que fue instanciada en ClaimsIdentity userPrincipal = new ClaimsIdentity(identity);
Hola, gracias justamente esto era lo que necesitaba.
Como recomendación, amplia un poco más la explicación ya que te saltas clases importantes que utilizas y solo viendo el proyecto se puede llegar a comprender. Para los que tenemos un poco de conocimiento se puede investigar, pero para novatos puede llegar a frustrarse con la explicación tan corta.
Nuevamente Muchas Gracias.
Donde hace la conexión a la base de datos
donde haces la conexión a la base de datos