Developer // Telecomunicaciones // Capoeira // Me gusta aprender de los que están cambiando vidas.

2013/07/03

El nuevo sistema de autenticación de ASP.NET

Microsoft es sin duda la empresa que mejor documentación tiene sobre sus tecnologías de desarrollo, y cuenta con la mejor herramienta: Visual Studio. Sin embargo hay un tema con el que nunca me había sentido satisfecho hasta hoy: la autenticación de ASP.NET y su integración con el modelo de negocio.

ASP.NET (MVC 4) ofrece un sistema de autenticación 'out of the box' muy útil, pero que en mi opinión carece de documentación que explique la mejor forma de integrarlo con otras entidades, entendiendo integración como la creación de relaciones uno a muchos. Estas son algunas propuestas que hay en internet y la razones por las que no me gustan:

1. Crear un proveedor personalizado que incluya las relaciones. Además de ser tedioso y susceptible a errores, implica desaprovechar lo que otros desarrolladores de mucha experiencia han hecho por nosotros.

2. Crear dos bases de datos, una para los usuarios y otra para el negocio (en esa parte estoy de acuerdo), y duplicar la información de los usuarios, asegurándose de crearlos con el mismo Id en ambas partes. Ese parece un buen acercamiento, excepto porque la base de datos que más utilizo, SQL Azure, no soporta transacciones distribuidas para asegurar que en ambos repositorios se va a crear correctamente el item. Entiendo que eso se podría solucionar con un trigger, pero la verdad es que no tengo ni idea de esas estructuras y soy fan de Code First.

3. No complicarse la vida, dejar todo en la misma base de datos y esperar que Code First haga su trabajo. Esa me encanta, pero en versiones anteriores de ASP.NET hay un inconveniente: la autenticación no es compatible con Code First, por lo que el "no complicarse la vida" cambiaba drásticamene por la necesidad de construir la base de datos a mano.

Es en el último punto que la nueva versión de ASP.NET la saca del estadio: el nuevo sistema de autenticación es 100% compatible con Code First; en el código fuente se puede ver el IdentityDbContext con sus respectivos DbSets para usuarios, roles y cuentas asociadas. Atrás quedó la SimpleMembership y su SimpleMembershipInitializer, y ni hablar de SQLMembership, que impedían a Code First hacer su trabajo.

Ahora escribir una relación es tan sencillo como:

public class Element
{
     [Key]
     [ScaffoldColumn(false)]
     public int Id { get; set; }
     public string Name { get; set; }
     public User User { get; set; }

}

Y basta con agregar una línea al DbContext:

public DbSet<WebApplication.Models.Element> Element { get; set; }

Incluso no es necesario agregarla a mano, porque el Scaffolding está en Click derecho > Agregar > Scaffold.

Un Create queda reducido a:

var usr = db.Users.Find(User.Identity.GetUserId());
element.User = usr;
db.Element.Add(element);
db.SaveChanges();

Porque ya le podemos decir a User.Identity que nos pase el Id del usuario gracias a una extensión que pueden encontrar en App_Start.IdentityConfig. Nada que ver con el anticuado Membership.GetUser(User.Identity.Name). Basta con agregar:

using Microsoft.AspNet.Identity

Y para los que están pensando en el escenario 2 existen las Code First Migrations, que funcionan muy bien cuando tenemos cambios en el modelo. Se habilitan con el comando:

Enable-Migrations -ContextTypeName Application.Models.DbContext

Pueden usar la nueva versión de ASP.NET (MVC 5) con Visual Studio 2013 y explorar las novedades en este tutorial. ¡Se los recomiendo! yo estoy muy satisfecho con los cambios... y eso que todavía está en beta.

2 comentarios:

  1. Buenas tardes Leonardo!

    Antes de nada, felicitarte por el articulo. Me ha venido bien para enterarme de lo nuevo de ASP MVC 5 :)

    Por otra parte tengo una duda que plantearte: en la versión 4 de MVC, a la hora de crear relaciones como la plateas, en mi caso utilizaba la siguiente manera:

    element.User = db.User.Find(WebSecurity.CurrentUserId);
    db.Element.Add(element);
    db.SaveChanges();

    La cual es muy parecida a la que planteas. Igualmente en la creación de la entidad la única diferencia que encuentro es que no se declara la propiedad User como "virtual", como se hacia en MVC 4.

    Por ello me gustaría que me aclarases cual es la verdadera diferencia que has encontrado al usar MVC 5 para estos usos.

    Por otra parte decirte que aun no he podido probar el nuevo VS2013 para ver los proyectos por defecto, pero si veo muy interesante que ahora el Membership venga creado a través de un DbContext por defecto, creo que esto permitirá realizar modificaciones sobre el Membership de manera más clara.

    Un saludo!

    ResponderEliminar
    Respuestas
    1. Luis, discúlpame por la demora, realmente hace poco habilité los comentarios y todavía no me acostumbro a revisarlos. Me alegra que te haya gustado el post, abajo respondo tus dudas:

      Sobre el primer punto quiero aclararte que nunca he implementado mi propio Membership Provider y siempre he preferido usar el que viene por defecto en ASP.NET MVC. De tu fragmento de código puedo intuir (dime si estoy equivocado) que la propiedad User está marcada como "virtual" y que tienes uno de dos escenarios: 1. no usas Code First, o 2. la relación element-user aparece en la lógica de la aplicación pero no está como una relación en la base de datos. Si usas Code First, por favor explícame cómo le hiciste, porque hasta ahora no había dado con una forma tan sencilla y "decente" de lograr el tan anhelado escenario #2.

      El segundo punto: la diferencia con MVC 5 es que volvieron a construir todo el sistema de autenticación y lo pusieron en el código fuente de las plantillas. Eso significa que es un código más "limpio" que se puede modificar fácilmente, cosa que no ocurría en las versiones anteriores de MVC. ¡Además puedes usar Code First desde el principio! eso es un alivio para los que no nos dedicamos al desarrollo Web. Quizá los procesos sean parecidos, como lo has demostrado con tu fragmento de código, pero va a ser mucho más sencillo personalizar la autenticación.

      Eliminar