Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

9
Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP .NET Personas que lo han encontrado útil: 30 de 42 - Valorar este tema Por Daniel Laco y Julieta Cozzi Descargar ejemplos de este artículo (120 KB) . Contenido Introducción Autenticación de Usuarios por Roles Usuario No Autenticado Usuario Autenticado Conclusión Introducción Hoy en día, si existe algo crítico en todo sistema informático, es el tema de la seguridad; particularmente en las aplicaciones Web, donde nos enfrentamos a una gran cantidad de ataques de los mas diversos tipos. Una de las necesidades más comunes de estos sistemas, además de los mecanismos propios de seguridad, es poder contar con un mecanismo de autorización basado en Roles o Perfiles que permita al sistema verificar qué roles tienen permisos sobre qué recursos; qué opciones de menú configurar; o qué información mostrar en tiempo de ejecución. En esta nota explicaremos una implementación de este modelo de seguridad utilizando la infraestructura que nos brinda ASP .NET, mediante la utilización de HttpModules , validando los Roles y Usuarios almacenados en una Base de Datos. Principio de la página Autenticación de Usuarios por Roles El trabajo con roles no es un tema nuevo en el desarrollo de aplicaciones, y existen distintas formas de implementarlo. ¿Recuerdas cuando en ASP había que chequear la seguridad página por página? Por supuesto que existen varias alternativas para tratar el mismo problema; algunas más eficientes y escalables que otras.

Transcript of Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

Page 1: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP .NETPersonas que lo han encontrado útil: 30 de 42 - Valorar este tema

Por Daniel Laco y Julieta Cozzi

 Descargar ejemplos de este artículo (120 KB).Contenido Introducción Autenticación de Usuarios por Roles Usuario No Autenticado Usuario Autenticado ConclusiónIntroducciónHoy en día, si existe algo crítico en todo sistema informático, es el tema de la seguridad; particularmente en las aplicaciones Web, donde nos enfrentamos a una gran cantidad de ataques de los mas diversos tipos.Una de las necesidades más comunes de estos sistemas, además de los mecanismos propios de seguridad, es poder contar con un mecanismo de autorización basado en Roles o Perfiles que permita al sistema verificar qué roles tienen permisos sobre qué recursos; qué opciones de menú configurar; o qué información mostrar en tiempo de ejecución.En esta nota explicaremos una implementación de este modelo de seguridad utilizando la infraestructura que nos brinda ASP .NET, mediante la utilización de HttpModules , validando los Roles y Usuarios almacenados en una Base de Datos. Principio de la páginaAutenticación de Usuarios por RolesEl trabajo con roles no es un tema nuevo en el desarrollo de aplicaciones, y existen distintas formas de implementarlo. ¿Recuerdas cuando en ASP había que chequear la seguridad página por página?Por supuesto que existen varias alternativas para tratar el mismo problema; algunas más eficientes y escalables que otras.En ASP .NET esto está resuelto ya que en el Web.Config se pueden configurar los accesos a las diferentes secciones de nuestra aplicación; por ejemplo, en la siguiente porción de código se indica que todos los usuarios tienen acceso a “página.aspx”:< location path ="pagina.aspx">

      < system.web >

            < authorization >

                  < allow users ="*" />

            </ authorization >

Page 2: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

      </ system.web >

</ location >

Pero, hay ocasiones en donde el escenario que se presenta es distinto, siendo necesario poder configurar dinámicamente el acceso a las páginas. Los usuarios, roles y páginas cambian continuamente por programación, en general, porque se almacenan en una base de datos, en un servicio de directorio como Active Directory o en alguna otra opción de almacenamiento.Para atender a estos casos veremos una implementación de seguridad basada en HttpModules de ASP .NET, donde los roles (o perfiles) de los usuarios están almacenados en una base de datos, y la autorización es administrada con esta solución.Primero debemos entender cómo funcionan y qué son estos HttpModules; para esto veamos el ciclo de vida de las páginas en ASP .NET y de qué modo interactúan estos módulos con el ciclo de vida de estas páginas:El ciclo de una llamada en HTTP comienza en el IIS (Internet Information Server), continúa cuando la petición es enviada a aspnet_isapi.dll que es la que administra todo el flujo entre IIS y ASP .NET.Cuando la petición ingresa en el circuito propiamente de ASP .NET, ésta pasa a través de los diferentes HttpModules configurados en la cadena, y por último la petición llega al HttpHandler. Este se ocupará del armado de la página y realizará la escritura del Html hacia el IIS, que a su vez la retorna al navegador que realizó la petición original (Ver Figura 1):

 Figura 1: Ciclo de vida de páginas ASP .NET.Volver al texto.

Dentro de este proceso, los HttpModules son filtros que se pueden programar e incluir en la configuración de la aplicación ASP .NET. Estos filtros permiten “atender” diferentes eventos que dispara ASP .NET durante el proceso de cada petición. Si miramos en el archivo machine.config de .NET veremos en la sección de HttpModules todos los filtros que vienen programados y configurados cuando se instala .NET; entre ellos están, por ejemplo, los que manejan las diferentes configuraciones de seguridad.            < add name ="WindowsAuthentication" type

="System.Web.Security.WindowsAuthenticationModule" />

            < add name ="FormsAuthentication" type

="System.Web.Security.FormsAuthenticationModule" />

            < add name ="PassportAuthentication" type

="System.Web.Security.PassportAuthenticationModule" />

       . . .

            < add name ="FileAuthorization" type

="System.Web.Security.FileAuthorizationModule" />

Page 3: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

Programar un HttpModule es sencillo, solo requiere implementar la interfaz IHttpModule y desde el código definir en cuáles eventos se va a responder. Una vez que el componente está programado, solamente es necesario agregar la entrada correspondiente en el .config de la aplicación. Más adelante trataremos este punto con mayor claridad.Si con .NET tenemos la posibilidad de poder “engancharnos” en el ciclo de vida de una página, es por demás interesante que utilicemos esta funcionalidad para nuestra solución de seguridad basada en roles.Veamos ahora el ejemplo para entender la solución:Para comenzar tenemos una base de datos con los usuarios que acceden a nuestra aplicación, los roles de los mismos y las páginas del sistema. Es decir, contaremos con una estructura mínima de 4 tablas, a saber (Ver Figura 2):

Usuarios: contiene todos los datos de los usuarios necesarios según las reglas del negocio de la aplicación y el identificador del rol al que pertenecen.

Perfiles: contiene todos los perfiles que se utilizarán en la aplicación y una descripción de los mismos.

Paginas: contiene todos los nombres y URLs de las páginas del sistema. PerfilesPaginas: contiene todas las páginas que utiliza cada rol.

 Figura 2. Volver al texto.

Necesitamos además una clase que contenga la información del usuario y el componente que cumpla la función de HttpModule.El método de autenticación estará basado en Forms. Ahora bien, ¿De qué forma podemos tener información del usuario autenticado en cualquier punto de la aplicación? Una de las alternativas podría ser guardar información en la Session del usuario. En este ejemplo reemplazaremos directamente el Principal del HttpContext por una clase propia que implementa la interfaz IPrincipal, además de tener las propiedades propias de la interfaz, agregaremos métodos y propiedades útiles para nuestro modelo de seguridad.Al personalizar este objeto a nuestras necesidades, nos quedará lo siguiente:

Principal: es un objeto que contiene información asociada al usuario actual; por ejemplo, su rol y su identidad, así como también otros datos que puedan ser de interés según las reglas del negocio.

Identidad ( Identity ): representa al usuario, tiene distintas propiedades que permiten obtener diferentes datos de los usuarios, por ejemplo:

                  System.Security.Principal.IPrincipal user;

                  string username = user.Identity.Name ;

Page 4: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

Nos permite obtener el nombre del usuario autenticado en nuestro sistema. Roles o Perfiles: simplemente son los nombres de los distintos roles que se agregan al objeto principal, los mismos pueden encontrarse en la base de datos.      public class FormsPrincipal :IPrincipal, IMyAppPrincipal

      {

            private IIdentity _identity;

            private string [] _roles;

            private string _Perfil;

            public FormsPrincipal( IIdentity identity, string [] roles)

            {

                  _identity = identity;

                  _roles = roles;

                  _Perfil = Perfil;

            }

            //...

            //Propiedad que utilizaremos para saber si el usuario tiene o no habilitado

            //el acceso a una determinada página

            public bool IsPageEnabled(string pageName)

            {

                  return Perfiles.IsPageEnabled( pageName, this._Perfil );

            }

            //...

      }

Cabe destacar que esta clase no está completa, solo se reprodujo parcialmente con el propósito de visualizar el objeto principal y cómo hemos implementado la interfaz IPrincipal.El paso siguiente es configurar y programar la autenticación basada en Forms:Agregamos la entrada en el web.config< authentication mode ="Forms">

      < forms loginUrl ="Login.aspx" timeout ="20"/>

</ authentication >

Y programamos nuestra página Login.aspx; hemos agregado los comentarios correspondientes a los efectos de clarificar la funcionalidad de cada línea de programación:            private void btnSubmit_Click(object sender, System.EventArgs e)

            {

                  string user = txtUser.Text;

                  string password = txtPassword.Text;

                  //Chequeo de usuario y contraseña

                  SeguridadEnAspNet.Usuario oUser = new SeguridadEnAspNet.Usuario();

                  string perfil = oUser.GetPerfil(user, password);

                  if (perfil.Length > 0) // perfil vacío significa que no fue encontrado

                  {

                        //Invoca a componente que se encarga del Cache de los datos

                        //en este caso de las páginas a las que el perfil tiene acceso

                        SeguridadEnAspNet.UserCache.AddPaginasToCache( perfil,

                              SeguridadEnAspNet.Perfiles.GetPaginas(perfil)

                              ,System.Web.HttpContext.Current );

Page 5: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

                        // Crea un ticket de Autenticación de forma manual,

                        // donde guardaremos información que nos interesa

                        FormsAuthenticationTicket authTicket =

                              new FormsAuthenticationTicket(2,  // version

                              user,

                              DateTime.Now,

                              DateTime.Now.AddMinutes(60),

                              false ,

                              perfil, // guardo el perfil del usuario

                              FormsAuthentication.FormsCookiePath);

                        // Encripto el Ticket.

                        string crypTicket = FormsAuthentication.Encrypt(authTicket);

                        // Creo la Cookie

                        HttpCookie authCookie =

                              new HttpCookie(FormsAuthentication.FormsCookieName,

                              crypTicket);

                        Response.Cookies.Add(authCookie);

                        // Redirecciono al Usuario - Importante!! no usar el RedirectFromLoginPage

                        // Para que se puedan usar las Cookies de los HttpModules

                        Response.Redirect( FormsAuthentication.GetRedirectUrl(user,false));

                  }

                  else

                        // Muestro mensaje de error

                        tblWarning.Style["display"] = "";

            }

Sólo hemos reproducido el código correspondiente al método del acceso; la clase completa la puedes consultar en el código adjunto a esta nota.Veamos ahora la programación de nuestro HttpModule.using System;

using System.Web;

using System.Security.Principal;

using System.Web.Security;

 

namespace SeguridadEnAspNet

{

      /// <summary>

      /// Modulo de Administración de la Seguridad

      /// Seguridad basada en Forms

      /// </summary>

      public class CustomAuthenticationModule : IHttpModule

      {

            public CustomAuthenticationModule()

            {}

            /// <summary>

            /// Inicializa el HTTPModule y asigna los EventHandlers a cada Evento

            /// Esta es la parte donde se define a que eventos va a atender el HttpModule

            /// </summary>

            /// <param name="oHttpApp"></param>

            public void Init(HttpApplication oHttpApp)

            {

                  // Se Registran los Manejadores de Evento que nos interesa

                  oHttpApp.AuthorizeRequest += new EventHandler(this.AuthorizaRequest);

Page 6: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

                  oHttpApp.AuthenticateRequest += new EventHandler(this.AuthenticateRequest);

            }

            public void Dispose()

            {}

            /// <summary>

            /// Administra la autorización por Request

            /// </summary>

            /// <param name="sender"></param>

            /// <param name="e"></param>

            private void AuthorizaRequest( object sender, EventArgs e)

            {    

                  if (HttpContext.Current.User != null)

                  {

                        //Si el usuario esta Autenticado

                        if (HttpContext.Current.User.Identity.IsAuthenticated)

                        {

                              if (HttpContext.Current.User is MyApp.Seguridad.FormsPrincipal)

                              {

                                    MyApp.Seguridad.FormsPrincipal principal =

                                                           (SeguridadEnAspNet.FormsPrincipal)

HttpContext.Current.User;

                                    //Se verifica si el Perfil del usuario tiene autorización para acceder a la

página

                                    if ( !principal.IsPageEnabled( HttpContext.Current.Request.Path) )

                                          HttpContext.Current.Server.Transfer( "NoAutorizado.aspx");

                              }

                        }

                  }

            }

            /// <summary>

            /// Autentica en Cada Request

            /// </summary>

            /// <param name="sender"> HttpApplication </param>

            /// <param name="e"></param>

            private void AuthenticateRequest(object sender, EventArgs e)

            {

                  if (HttpContext.Current.User != null)

                  {

                        //Si el usuario esta Autenticado

                        if (HttpContext.Current.User.Identity.IsAuthenticated)

                        {

                              if (HttpContext.Current.User.Identity is FormsIdentity)

                              {

                                    //Traigo el Rol que esta guardado en una Cookie encriptada

                                    FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;

                                    FormsAuthenticationTicket ticket = id.Ticket;

 

                                    string cookieName =

                                          System.Web.Security.FormsAuthentication.FormsCookieName;

 

                                    string userData =

                                         

System.Web.HttpContext.Current.Request.Cookies[cookieName].Value;

Page 7: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

 

                                    ticket  = FormsAuthentication.Decrypt(userData);

 

                                    string rol="";

                                    if( userData.Length > 0 )

                                          rol= ticket.UserData;

 

                                    //Se crea la clase Principal  y se asigna al CurrenUser del Contexto

                                                      HttpContext.Current.User = new

                                    SeguridadEnAspNet.FormsPrincipal(_identity, perfil);                         

                              }

                        }

                  }

            }//AuthenticateRequest

      } //class

} //namespace

Una vez que tenemos nuestro módulo de seguridad (HttpModule:

CustomAuthenticationModule), se deberá referenciar en el archivo de configuración de

nuestra aplicación como se muestra a continuación:

< authentication mode ="Forms">

      < forms loginUrl ="Login.aspx" timeout ="20" />

</ authentication >

< authorization >

      < deny users ="?" />

</ authorization >

//...

<! -- Modulo de Autorización -- >

< httpModules >

      < add type ="SeguridadEnAspNet.CustomAuthenticationModule, SecurityModules" name

="CustomAuthenticationModule" />

</ httpModules >

De esta forma estamos configurando el modo de autenticación por formularios, indicándole que la página de autenticación es “Login.aspx” y que los usuarios anónimos no tienen acceso a nuestra aplicación. También estamos definiendo el HttpModule que utilizaremos, en este caso, para administrar la seguridad de la aplicación.Con esta configuración, ¿Qué ocurre cuando un usuario realiza la petición de una página de nuestra aplicación? Principio de la páginaUsuario No AutenticadoSi el usuario no está autenticado aún, realiza el proceso normal de autenticación por Formularios (Forms). Principio de la páginaUsuario AutenticadoSi el usuario está autenticado, el primer evento que se dispara es el AuthenticateRequest, para obtener los roles (o perfiles) del usuario desde una cookie, y con esta información creamos la claseFormsPrincipal. Este objeto será posteriormente asignado al HttpContext.CurrentUser.El evento que se dispara a continuación es el AuthorizaRequest. Lo que hace es verificar si el usuario tiene acceso a la página que está solicitando, en caso de que

Page 8: Autenticación de Usuarios basada en Roles utilizando HTTPModules en ASP

no esté autorizado, la aplicación lo derivará a una página de error con el mensaje correspondiente.Si bien la funcionalidad que estamos mostrando se aplica para páginas, también se puede extender a la clase que implementa IPrincipal y agregarle otros métodos que permitan, por ejemplo, chequear a qué datos de una página tiene acceso el rol. Para esto, en cualquier parte de nuestro código se puede hacer lo siguiente:                  SeguridadEnAspNet.CustomPrincipal  user =

                        (SeguridadEnAspNet.CustomPrincipal) HttpContent.CurrentUser;

                  if ( ! user.IsDataVisible("txtNombreControl" ))

                        txtNombreControl.Visible = false;