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

This Post Has 4 Comments

  1. Crys Reply

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

  2. Francisco Reply

    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.

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.