Está en la página 1de 19

ASP.

NET Core Razor con autenticación de usuarios

En esta práctica se muestra cómo crear una aplicación web ASP.NET Core con datos de usuario protegidos por
autorización. Se usará el modelo “Alumnos” de la práctica pasada y un nuevo modelo “Contactos”, esto con la finalidad
de representar los pasos para proteger cada una de las páginas y directorios.

Para poder realizarla, se necesita implementar Identity en ASP.NET Core, la cual tiene las siguientes caracteristicas:

 Es una API que admite la funcionalidad de inicio de sesión de la interfaz de usuario (UI).
 Administra usuarios, contraseñas, datos de perfil, roles, notificaciones, tokens, confirmación por correo
electrónico, etc.

Los usuarios pueden crear una cuenta con la información de inicio de sesión almacenada en Identity o pueden usar un
proveedor de inicio de sesión externo. Los proveedores de inicios de sesión externos admitidos incluyen cuentas de
Facebook, Google, Microsoft y Twitter.

Identity normalmente se configura mediante una base de datos SQL Server para almacenar nombres de usuario,
contraseñas y datos de perfil. Como alternativa, se puede usar otro almacén persistente, por ejemplo, Azure Table
Storage.

Para ambos modelos se mostrar una lista de alumnos (en la raíz del proyecto) y de contactos (en un subdirectorio) que
han creado los usuarios autenticados (registrados). Hay tres grupos de seguridad:

 Los usuarios registrados (Sin rol, pero se puede considerar como Rol Users) pueden ver todos los datos
aprobados en el modelo “Contactos” y pueden editar o eliminar sus propios datos. En el modelo Alumnos solo
pueden ver todos los datos.
 Los Gerentes (Rol Manager) pueden aprobar o rechazar los datos de contacto. Solo los contactos aprobados son
visibles para los usuarios. También pueden modificar la información de los Alumnos
 Los Administradores (Rol Administrator) pueden aprobar, rechazar y editar o eliminar los datos. El
administrador tiene todos los privilegios. Puede leer, editar o eliminar cualquier contacto, alumno y cambiar el
estado de los contactos.

Como nota inicial, este proyecto también se realizará con la ideología Code First.

Al abrir Visual Studio, aparecerá la siguiente pantalla donde se debe elegir “Crear un proyecto”:
Se elegirá “Aplicación web ASP.NET Core”

Esta aplicación se llamará “CoreRazorUsers”

Crearemos una “Aplicación web” simple sin MVC aunque seguiremos muchas convenciones.

Cabe mencionar que a diferencia del proyecto anterior aquí ocuparemos Autenticación por “cuentas individuales”.

El proyecto generado proporciona ASP.NET Core Identity como biblioteca Razor de clases. La biblioteca de clases
IdentityRazor expone puntos de conexión con el área Identity. Por ejemplo:

 /Identity/Account/Login
 /Identity/Account/Logout
 /Identity/Account/Manage

Con esto se iniciará la creación del proyecto.

Entity Framework Core

Las aplicaciones .NET Core, como ya se mencionó anteriormente, no usan el framework .NET completo, solo módulos o
partes administradas por Nuget.

En el proyecto anterior, no había paquetes agregados desde la creación, en cambio en este proyecto con autenticación
si se pueden observar varios paquetes instalados desde la creación del proyecto:
Para esta práctica es necesario agregar los siguientes módulos:

 Microsoft.EntityFrameworkCore
 Microsoft.EntityFrameworkCore.Desginer

Para ello, en el menú Herramientas, se debe acceder a la opción Administrador de paquetes NuGet, y en el menú que
se desplegara, elegir: Administrar paquetes NuGet para la solución.

Escriban los paquetes (se listan arriba) a descargar, en la imagen se muestran los 3, deben instalar uno a la vez, en el
recuadro de la derecha por cada paquete marquen el proyecto y procedan con el botón Instalar. Miren la nota en la
siguiente página.
En cada paquete les pedirá aceptar la licencia de módulos.

Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.Design

Comprobación: en la pestaña “Instalado”, deben aparecer las siguientes librerías

Primeros pasos:

Como ya se habló antes, las aplicaciones Code First, implican que la base de datos se creara en función del código y no al
revés, por lo tanto debemos iniciar con la creación de una cadena de conexión, para ello editaremos el archivo
“appsettings.json” y como se vio en la práctica anterior, se debe agregar una cadena de conexión.

Pero, en este tipo de proyectos ya incluyen una cadena de conexión:

Para poder usar SQL Server, se modificarán los datos del manejador y la base de datos:
Cabe mencionar que la cadena JSON llamada Conexión contiene los siguientes valores:

 Server: Nombre de su servidor SQL Server (CÁMBIENLO).


 Database: nombre de la base de datos, aunque no está creada la usaremos.
 User Id: usaremos a “sa”.
 Password: la contraseña de “sa”
 Trusted_Connection: para marcarla como confiable.
 MultipleActiveResultSets: múltiples conexiones simultaneas.

Modelos

Por best practices, se debe crear una carpeta llamada Models.

Sobre el proyecto, presionar clic derecho y en la opción Agregar, desplegar el submenú y seleccionar Nueva Carpeta.

Inmediatamente creara la nueva carpeta la cual deben renombrar como Models.

Dentro de esta carpeta se guardan los Modelos que usaremos.


Sobre la carpeta Models, presionar clic derecho, seleccionar Agregar, y elegir Clase.

La primera clase que se creara se llamara “Alumnos.cs”

Esta clase se muestra a continuación:


La segunda clase que se creara se llamara “Contactos.cs”

Esta clase se muestra a continuación:


Cabe mencionar que ambos modelos cuentan con el campo OwnerID, que servirá para relacionarlos con el propietario
de la información.

Contextos

Cada Modelo debe contar con un Contexto de Base de datos, Estos contextos no hay ninguna regla que defina su
ubicación, pero para este tipo de proyectos ya está creado un contexto, con una migración (esta migración es de
identity) en una carpeta para los Contextos de base de datos llamada Data.

Antes de continuar, se deben crear las tablas de identity Framework, por lo tanto, en la consola de Nuget se debe
ejecutar:

Al concluir ya podemos analizar y trabajar en “ApplicationDbContext.cs”, esta clase se muestra a continuación:

Una instancia DbContext representa una combinación de una unidad de trabajo y un reposito de patterns los cuales pueden ser
usados para realizar querys a una tabla en la base de datos definida en alguna cadena de conexión ( get), así mismo también se
pueden escribir datos y realizar cambios (set).
Controladores de Autenticación.

Se deben crear los siguientes controladores de autorización:

 ContactosIsOwnerAuthorizationHandler: garantiza que un usuario solo puede editar sus datos.


 ContactosManagerAuthorizationHandler: permite a los Gerentes aprobar o rechazar contactos.
 ContactosAdministratorsAuthorizationHandler: permite a los administradores aprobar o rechazar contactos y
editar o eliminar contactos.
 AlumnosIsOwnerAuthorizationHandler: garantiza que un usuario solo puede editar sus datos.
 AlumnosManagerAuthorizationHandler: permite a los Gerentes usar todos los datos de Alumnos.
 AlumnosAdministratorsAuthorizationHandler: permite a los administradores aprobar o rechazar contactos y
editar o eliminar contactos.

Para implementarlos se debe crear una nueva carpeta en la raíz del proyecto

Esta carpeta se debe llamar “Authorization”

Dentro de ella se debe crear las siguientes clases:


Authorization/Constants.cs

En esta clase se definen las acciones a realizar por cada rol y algunas por modelos, solo como etiquetas.

Authorization/Operations.cs

En esta clase se definen las autorizaciones a las operaciones

Authorization/ContactosAdministratorsAuthorizationHandler.cs
Authorization/ContactosIsOwnerAuthorizationHandler.cs

Authorization/ContactosManagerAuthorizationHandler.cs

Authorization/AlumnosAdministratorAuthorizationHandler.cs
Authorization/AlumnosIsOwnerAuthorizationHandler.cs

Authorization/AlumnosManagerAuthorizationHandler.cs
Configuración de la cuenta de prueba

Se debe crear una clase SeedData, la cual crea dos cuentas: Administrator y Manager.

Antes de crear esta clase, se debe usar la herramienta Administrador de secretos para establecer una contraseña para
estas cuentas. Para ello sobre el proyecto, se debe presionar el botón derecho del mouse y elegir: Abrir en Terminal

Esto abrirá una consola powershell para desarrolladores, en la cual se debe agregar a los secretos de usuario una
contraseña compleja, esa contraseña debe ser por lo menos de 8 caracteres, con mayusculas, minúsculas, números y
símbolos, se usará para crear los usuarios iniciales, en este caso se usará “NETC0re*”, pero pueden usar la que gusten,
solo no la olviden, esto se realizara con el comando:

Posteriormente se debe crear la clase SeedData, la cual se muestra a continuación:


Data/SeedData.cs

Esta clase la dejare en código, aquí se crearán los usuarios iniciales:

 admin@net.com que será el administrador


 manager@net.com que será el gerente
 user@net.com que será un usuario convencional

Los 3 usuarios tendrán como contraseña NETC0re*

También se crearán 5 datos iniciales para los modelos Contactos y Alumnos

using CoreRazorUsers.Authorization;
using CoreRazorUsers.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace CoreRazorUsers.Data
{
public static class SeedData {
public static async Task Initialize(IServiceProvider serviceProvider, string testUserPw) {
using (var context = new ApplicationDbContext(serviceProvider.GetRequiredService<DbContextOptions<ApplicationDbContext>>())) {
// For sample purposes seed both with the same password.
// Password is set with the following:
// dotnet user-secrets set SeedUserPW <pw>

// The admin user can do anything


var adminID = await EnsureUser(serviceProvider, testUserPw, "admin@net.com");
await EnsureRole(serviceProvider, adminID, Constants.ContactosAdministratorsRole);
await EnsureRole(serviceProvider, adminID, Constants.AlumnosAdministratorsRole);

// allowed user can create and edit contacts that they create
var managerID = await EnsureUser(serviceProvider, testUserPw, "manager@net.com");
await EnsureRole(serviceProvider, managerID, Constants.ContactosManagersRole);
await EnsureRole(serviceProvider, managerID, Constants.AlumnosManagersRole);

var userID = await EnsureUser(serviceProvider, testUserPw, "user@net.com");

SeedDB(context, adminID);
SeedDB(context, managerID);
SeedDB(context, userID);
}
}

private static async Task<string> EnsureUser(IServiceProvider serviceProvider, string testUserPw, string UserName) {
var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

var user = await userManager.FindByNameAsync(UserName);


if (user == null) {
user = new IdentityUser {
UserName = UserName,
EmailConfirmed = true
};
await userManager.CreateAsync(user, testUserPw);
}

if (user == null) {
throw new Exception("No es una contrasea fuerte!");
}

return user.Id;
}

private static async Task<IdentityResult> EnsureRole(IServiceProvider serviceProvider, string uid, string role) {
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

if (roleManager == null) {
throw new Exception("roleManager null");
}

IdentityResult IR;
if (!await roleManager.RoleExistsAsync(role)) {
IR = await roleManager.CreateAsync(new IdentityRole(role));
}

var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();

var user = await userManager.FindByIdAsync(uid);

if (user == null) {
throw new Exception("The testUserPw password was probably not strong enough!");
}

IR = await userManager.AddToRoleAsync(user, role);


return IR;
}

public static void SeedDB(ApplicationDbContext context, string adminID) {

// Verificar si ya hay Contactos, si no los hay agrega los iniciales


if (!context.Contactos.Any()) {
context.Contactos.AddRange(
new Contactos
{
Name = "Debra Garcia",
Address = "1234 Main St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "debra@example.com",
Status = ContactStatus.Approved,
OwnerID = adminID
},
new Contactos
{
Name = "Thorsten Weinrich",
Address = "5678 1st Ave W",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "thorsten@example.com",
Status = ContactStatus.Submitted,
OwnerID = adminID
},
new Contactos
{
Name = "Yuhong Li",
Address = "9012 State st",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "yuhong@example.com",
Status = ContactStatus.Rejected,
OwnerID = adminID
},
new Contactos
{
Name = "Jon Orton",
Address = "3456 Maple St",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "jon@example.com",
Status = ContactStatus.Submitted,
OwnerID = adminID
},
new Contactos
{
Name = "Diliana Alexieva-Bosseva",
Address = "7890 2nd Ave E",
City = "Redmond",
State = "WA",
Zip = "10999",
Email = "diliana@example.com",
OwnerID = adminID
}
);
context.SaveChanges();
}

// Verificar si ya hay Alumnos, si no los hay agrega los iniciales


if (!context.Alumnos.Any()) {

context.Alumnos.AddRange(
new Alumnos
{
NC = "00000001",
Nombre = "Alumno 1",
DeudaBiblioteca = 10,
Promedio = 90.1f,
Inscrito = AlumnoStatus.Inscrito,
OwnerID = adminID
},
new Alumnos
{
NC = "00000002",
Nombre = "Alumno 2",
DeudaBiblioteca = 11,
Promedio = 91.1f,
Inscrito = AlumnoStatus.Inscrito,
OwnerID = adminID
},
new Alumnos
{
NC = "00000003",
Nombre = "Alumno 3",
DeudaBiblioteca = 13,
Promedio = 93.1f,
Inscrito = AlumnoStatus.Inscrito,
OwnerID = adminID
},
new Alumnos
{
NC = "00000004",
Nombre = "Alumno 4",
DeudaBiblioteca = 14,
Promedio = 40.1f,
Inscrito = AlumnoStatus.Inscrito,
OwnerID = adminID
},
new Alumnos
{
NC = "00000005",
Nombre = "Alumno 5",
DeudaBiblioteca = 15,
Promedio = 90.5f,
Inscrito = AlumnoStatus.Inscrito,
OwnerID = adminID
}
);
context.SaveChanges();
}
}
}
}
Configurar Identity

En Program.cs se deben establecer las siguientes líneas


Migración y Actualización

En este tópico, migración se refiere a crear los objetos en el manejador de base de datos desde Los modelos Alumnos y
Contactos.

Antes de avanzar, se debe Recompilar el proyecto con 0 errores

Para crear los objetos en la Base de Datos, se debe crear una migración desde la “Consola del Administrador de
paquetes”, para ello se debe desplegar el menú Herramientas, elegir la opción “Administrador de paquetes NuGet” y
del submenú elegir “Consola del Administrador de paquetes”.

Esto desplegara una consola en la parte inferior del IDE:


En ella debemos ejecutar el comando para crear la migración de los modelos:

add-migration MigracionModelos

Esto creara una nueva migración dentro de la carpeta Data/Migrations, con algunos archivos, como se muestra en la
siguiente imagen.

Como un punto extra, en esta migración se agregaran índices únicos para evitar crearlos por código:

Al concluir compilar y ejecutar update-database:


Esto creara en SQL Server lo siguiente:

Una base de datos llamada CoreRazorUsers con una tabla llamada Alumnos y otra tabla llamada Contactos, las cuales
contienen los campos definidos en el modelo y sus datos iniciales

También se verán reflejadas las tablas de Identity

Entregar: Capturas (a pantalla completa) de la migración y la base de datos.

También podría gustarte