Autor en Google+
Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

10 años online 🎂

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET, ASP.NET Core, MVC, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 22 de mayo de 2012
ASP.NET MVCDesde el principio de los tiempos, ASP.NET MVC dispone de un mecanismo muy sencillo para controlar el acceso a acciones, basado en los sistemas de autenticación por formulario estándar de ASP.NET.


A grandes rasgos, el asunto consiste en decorar acciones, o incluso los controladores completos, con el atributo [Authorize], de forma que si el usuario no ha superado el procedimiento de autenticación, no se podrá acceder a ellas. Además, gracias a los parámetros admitidos por este filtro, es posible indicar qué usuarios concretos pueden ejecutar la acción, o qué roles son los permitidos:
public class CustomersController : Controller
{
    [Authorize(Roles = "manager")]
    public ActionResult Delete(int id)
    {
        // TODO: Delete a customer
    }

    //...
}
Sin embargo, había algunos escenarios que eran algo molestos de implementar usando esta técnica. Imaginad que estáis desarrollando una web totalmente privada, en la que es necesario incluir el atributo [Authorize] a todos los controladores: el ideal sería utilizar filtros globales, ¿no? En vez de modificar cada uno de los controladores, haríamos lo siguiente en el global.asax.cs:
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
Y como casi todo en la vida, no es tan sencillo. Este atributo afectaría a las acciones que retornan las vistas del propio formulario de login, por lo que simplemente no sería posible acceder a la aplicación. Lógicamente hay varias soluciones para este problema; por ejemplo, podríamos introducir a mano el filtro en los controladores, o incluso mejor aún, crear un controlador base con el atributo y heredar de él todos los controladores menos los destinados a facilitar la identificación del usuario.

ASP.NET MVC 4 introduce un nuevo filtro, denominado [AllowAnonymous] cuya utilidad seguro que podéis deducir en este momento: aplicado a un controlador o acción, hace que se ignore en éste cualquier posible atributo [Authorize] que pudiera haberse aplicado al mismo. De hecho, en la misma plantilla de proyectos MVC 4 ya vemos que se utiliza bastante en el controlador AccountController , tradicionalmente destinado a realizar las tareas de conexión, registro y desconexión de usuarios:
    [Authorize]
    public class AccountController : Controller
    {

        //
        // GET: /Account/Login

        [AllowAnonymous]
        public ActionResult Login()
        {
            // ... 
        }
    }
Y este post podría acabar aquí, pero hay una curiosidad que quería comentar sobre la forma en que está implementado este filtro. Si observamos su código fuente, vemos que es, poco más o menos, así:
namespace System.Web.Mvc 
{  
   [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
                   AllowMultiple = false, Inherited = true)] 

   public sealed class AllowAnonymousAttribute : Attribute 
   { 

   } 
}
Salvo la metainformación contenida en el AttributeUsage, el resto es una clase vacía (!). Aunque al principio puede parecer raro, es muy lógico: la magia la pone el atributo [Authorize] en cuya implementación encontramos la lógica que posibilitará la ejecución de una acción si se encuentra decorada (ella o su controlador) con [AllowAnonymous], saltándose todas las comprobaciones de autorización:
bool skipAuthorization = 
    context.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
    || context.ActionDescriptor.ControllerDescriptor.IsDefined(
                typeof(AllowAnonymousAttribute), inherit: true);

if (skipAuthorization)
{
    return; 
}

Publicado en Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

7 Comentarios:

Pablo dijo...

Interesante, gracias por el aporte

Sebastian dijo...

Jose, dado que jon galloway http://weblogs.asp.net/jgalloway/default.aspx definio a simplemembership como el futuro de asp.net, sabes si es posible incorporar anonymous identifications extendiendo los methods de la clase simplemembership provider http://msdn.microsoft.com/en-us/library/webmatrix.webdata.simplemembershipprovider(v=vs.111) usando el codigo open source contenido en http://aspnetwebstack.codeplex.com ? siempre he preferido guardar la informacion del usuario - eg: lenguaje- en base de datos en vez de en cookies pero no esta presente esa opcion para usuarios anonimos en ese provider.
muchas gracias por la ayuda!
sebastian

José M. Aguilar dijo...

Hola, Sebastián.

Pues sí, la verdad es que SimpleMembership está muy bien, o al menos mucho mejor que la opción que teníamos antes.

Sin embargo, hasta lo que he podido ver, no hay forma de integrar al usuario anónimo de una forma similar a como hacíamos con los proveedores habituales de ASP.NET.

Probablemente lo más sencillo sea generar un id único del usuario y hacerlo persistir entre llamadas usando cookies o parámetros de rutas, y a partir de ahí hacer persistentes sus datos extendidos en la base de datos.

Suerte & un saludo!

Sebastian dijo...

gracias Jose, si yo estaba pensando en persistirlos a traves de un guid en la tabla user que tome el cookie asignado al usuario. voy a probarlo. muchas gracias!

Camilo Cabrales dijo...

Buenas tardes,
Tengo una pregunta, estoy construyendo servicios con WEB API, pero quiero validar que un valor que envió como parámetro, si el valor no es valido debería enviar un error o HttpStatusCode.Unauthorized.
Lo estoy haciendo con una clase que extiende DelegatingHandler para validar ese dato pero cuando es una petición GET o deja pasar.
Alguna idea de como es la forma correcta de hacerlo?

Gracias

José M. Aguilar dijo...

Hola, Camilo.

No puedes usar los mecanismos de validación habituales?

Saludos.

José M. Aguilar dijo...

Hola, Camilo.

No puedes usar los mecanismos de validación habituales?

Saludos.