En este post vamos a explicar el tema de la seguridad personalizada sobre un proyecto MVC. Los objetivos serán:

  • Aplicar la seguridad sobre un usuario y una contraseña que nosotros decidamos.
  • Implementar la seguridad basándonos en roles.
  • Permitir solamente entrar en una “zona segura” al usuario con rol de ADMINISTRADOR.

Para acceder a la zona segura usaremos tres credenciales diferentes:

  • Usuario: ADMIN   Contraseña: ADMIN
  • Usuario: USER   Contraseña: USER
  • Usuaro: PRUEBA   Contraseña: PRUEBA

Comenzamos creando un nuevo proyecto en Visual Studio yendo a File > New > Project.
Elegimos un proyecto de Visual C# > Web > ASP.NET Web Application al que llamaré SeguridadPersonalizadaMVC.
Aceptamos y en la nueva ventana que se nos mostrará seleccionamos la plantilla MVC.
Pulsamos Ok y ya tendremos nuestro proyecto MVC creado. Deberíamos ver el siguiente árbol de archivos:

Login

Dentro de esta carpeta crearemos una vista llamada Login.cshtml que es donde crearemos los textbox de usuario y contraseña que tendrá que rellenar la persona que quiera acceder a la zona segura. Para hacerlo pulsamos botón derecho sobre Validacion > Add > View… y la llamamos Login.cshtml. A continuación hacemos el diseño sobre la vista:

login

@{
ViewBag.Title = "Login";
}
<h2>Validación de usuarios</h2>

<form method="post">
   <div>
      <label>Usuario</label>
      <input type="text" name="usuario" class="form-control" placeholder="Nombre de usuario" />
   </div>
   <div>
      <label>Password</label>
      <input type="text" name="password" class="form-control" placeholder="Password" />
   </div>
   <div>
      <button type="submit" class="btn-success">Entrar</button>
   </div>
</form>
<hr />
<h2 style="color:red">@ViewBag.Mensaje</h2>

Dentro de Validación creamos otra vista llamada ErrorAcceso.cshtml que será la vista a la que nos lleve en caso de no cumplir las condiciones de acceso y hacemos el siguiente diseño:

ErrorAcceso

@{
ViewBag.Title = "ErrorAcceso";
}
<h2 style="color:red">No tiene permisos para acceder a la ZONA SEGURA</h2>

Creamos otra carpeta dentro de Views a la que llamaremos ZonaSegura y dentro de esta carpeta haremos la clase Index.cshtml que será la vista a la que nos lleve en caso de cumplir las condiciones de acceso y hacemos el siguiente diseño:

ZonaSegura

@{
ViewBag.Title = "Index";
}
<h2>Bienvenido a la ZONA SEGURA</h2>

A continuación tendremos que crear los controllers para las dos zonas que hemos hecho previamente (ZonaSegura y Validacion). Para ello tendremos pulsamos botón derecho sobre Controllers > Add > Controller… y le llamamos ValidacionController.cs. Dentro de la clase ValidacionController añadimos los GET para mostrar las vistas y el POST del login que se ejecutará al pulsar el botón Enviar del formulario:

// GET: Login
public ActionResult Login()
{
   return View();
}

// GET: ErrorAcceso
public ActionResult ErrorAcceso()
{
   return View();
}

// POST: Login
[HttpPost]
public ActionResult Login()
{
   return View();
}

De la misma manera creamos el controlador ZonaSeguraController.cs.

Ahora vamos a crear nuestro modelo con el que sabremos si el usuario que intenta acceder existe o no. Eso lo haremos con un método al que llamaremos ExisteUsuario el cual tras comprobar el usuario y la contraseña le asignará un rol (ADMINISTRADOR o USUARIO) y devolverá un true, esto significará que el usuario existe, y en caso contrario devolverá un false. Para ello, sobre la carpeta Models creamos una clase llamada ValidarUsuario.cs y le añadimos:

public String Role { get; set; }
public bool ExisteUsuario(String user, String pass)
{
   if(user.ToUpper() == "ADMIN" && pass.ToUpper() == "ADMIN")
   {
      this.Role = "ADMINISTRADOR";
      return true;
   }else if(user.ToUpper() == "USER" && pass.ToUpper() == "USER")
   {
      this.Role = "USUARIO";
      return true;
   }else
   {
      return false;
   }
}

Volvemos a nuestro ValidacionController.cs y añadimos las siguientes librerías:

using System.Web.Mvc;
using SeguridadEmpleados.Models;
using System.Web.Security;

Y modificamos el POST del Login. Lo que haremos será que si al llamar al método ExisteUsuario nos devuelve True creamos un ticket, el cual tiene los siguientes parámetros:

  • Versión, nombre del ticket, inicio del ticket, final del ticket, persistencia para la cookie, datos del usuario, ruta para almacenar la cookie.

Una vez creado tenemos que almacenar el ticket dentro de la cookie en el servidor. Para ello primero se encripta con Encrypt, después se crea la cookie y después se le añade el ticket mediante Add. Por último se envía al usuario al Index de la ZonaSegura.

En caso de que al llamar al método ExisteUsuario nos devuelva False nos mostrará un mensaje diciendo que el usuario o la contraseña son incorrectos y nos mantendrá en el Login:

UsuarioPasswordIncorrecto

// POST: Login
[HttpPost]
public ActionResult Login(String usuario, String password)
{
   ValidarUsuario modelo = new ValidarUsuario();
   if(modelo.ExisteUsuario(usuario, password))
   {
      FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, usuario, DateTime.Now, DateTime.Now.AddMinutes(8), true 
      , modelo.Role, FormsAuthentication.FormsCookiePath);
      String datos = FormsAuthentication.Encrypt(ticket);
      HttpCookie c = new HttpCookie("ticket", datos);
      this.Response.Cookies.Add(c);
      return RedirectToAction("Index", "ZonaSegura");
   }
   else
   {
      ViewBag.Mensaje = "Usuario/Password incorrectos";
      return View();
   }
}

En este momento vamos a modificar el archivo Global.asax. Primero vamos a agregar las librerías que vamos a utilizar:

using System.Web.Security;
using System.Security.Principal;
using System.Web.Routing;

Lo que haremos aquí será crearnos el método PostAuthenticateRequest, después recuperaremos la cookie a la que antes llamamos ticket y comprobaremos que exista, en cuyo caso afirmativo nos estará indicando que ya nos hemos validado y tenemos un ticket. Desencriptamos el ticket con Decrypt, extraemos el nombreusuario y el rol. Creamos un usuario GenericPrincipal que estará formado por un  GenericIdentity y un rol. Y por último almacenaremos al usuario en el sistema con Current.User:

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
   HttpCookie cookie = Request.Cookies["ticket"];
   if(cookie != null)
   {
      FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
      String nombreusuario = ticket.Name;
      String rol = ticket.UserData;
      GenericIdentity id = new GenericIdentity(nombreusuario);
      GenericPrincipal user = new GenericPrincipal(id, new String[] { rol });
      HttpContext.Current.User = user;
   }
}

Una vez hemos hecho lo anterior nos creamos, dentro del proyecto, una carpeta a la que llamaremos Autorizacion. Y dentro una clase llamada AutorizacionPersonalizadaAttribute.cs. Este paso es muy importante para la implementación de nuestra seguridad ya que es donde se indicarán los roles que pueden acceder a nuestra zona segura . En primer lugar haremos que la clase herede de AuthorizeAttribute y después sobrescribiremos su método OnAuthorization. Dentro de este método preguntaremos si el usuario ya se ha validado y en caso afirmativo recuperaremos el usuario que almacenamos previamente en el archivo Global.asax. Preguntamos por el rol del usuario y si este no se corresponde con ADMINISTRADOR le llevaremos a la vista ErrorAcceso, y si el usuario no se ha validado todavía le llevaremos a la página de Login. Para ello añadimos las siguientes librerías:

using System.Security.Principal
using System.Web.Routing;

Y el siguiente código:

public override void OnAuthorization(AuthorizationContext filterContext)
{
   if (filterContext.HttpContext.Request.IsAuthenticated)
   {
      GenericPrincipal usuario = HttpContext.Current.User as GenericPrincipal;
      if(usuario.IsInRole("ADMINISTRADOR") == false)
      {
         filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Validacion", action = "ErrorAcceso" }));
      }
   }else
   {
      filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Validacion", action = "Login" }));
   }
}

Vamos a aplicar nuestra seguridad, para ello vamos al controlador ZonaSeguraController.cs y añadimos la siguiente biblioteca:

using SeguridadPersonalizada.Autorizacion;

Y le añadimos al GET de ZonaSegura nuestra decoración, quedando de la siguiente forma:

// GET: ZonaSegura
[AutorizacionPersonalizada]
public ActionResult Index()
{
   return View();
}

Por último vamos a Views > Shared > _Layout.cshtml y añadimos un ActionLink en la barra de navegación:

@Html.ActionLink("Zona Segura", "Index", "ZonaSegura")

De forma opcional podemos hacer también un botón para desloguearnos. A continuación  de lo anterior añadimos otro ActionLink:

@Html.ActionLink("LogOut", "CerrarSesion", "Validacion")

Y en ValidacionController.cs hacemos el GET que le corresponde:

// GET: CerrarSesion
public ActionResult CerrarSesion()
{
   Response.Cookies["ticket"].Expires = DateTime.Now.AddYears(-90);
   return RedirectToAction("Index", "Home");
}

De esta forma terminamos de implementar la seguridad sobre nuestro proyecto MVC basándonos en roles.

Una forma de mejorar este ejemplo pasaría por hacer una consulta a una base de datos que nos proporcionara el rol del usuario que intenta loguearse.


Descripción: Mostramos la forma de implantar seguridad personalizada sobre un proyecto MVC basándonos en los roles de los usuarios que intentan entrar.

Autor: Jorge Bravo Sayago

Curso: Microsoft MCSD Web Applications + SharePoint Apps

Centro: Tajamar

Año académico: 2016-2017