Twitter Facebook RSS Feed

lunes, 19 de octubre de 2009 a las 10:59hs por Dario Krapp

Prólogo e inicios

En algunas tecnologías web antiguas, como por ejemplo ASP (en el caso de Microsoft) la programación de la autenticación de los usuarios quedaba en manos de los programadores. A pesar de que no es una tarea extremadamente compleja, demandaba tiempo de desarrollo, pruebas y correcciones por cada sitio que se desarrollaba y aunque las técnicas utilizadas para esta funcionalidad se encontraban informalmente estandarizadas, siempre podían existir diferencias en las implementaciones (quien haya caído en la suerte de tener que modificar una aplicación legada, posiblemente sabrá a que nos referimos ), lo que influía en los tiempos y costos del desarrollo y si lo analizamos parecería tener poco sentido el gasto de esfuerzos de desarrollo en una tarea que es tan repetitiva. Quizás esa sea una de las causas por las que ASP.NET provee un sistema de autenticación ya incluido, lo que implica código, desarrollado, testeado, mantenido y listo para usar. En este artículo nos adentraremos en la autenticación por formularios desde sus comienzos en las primeras versiones de ASP.NET hasta la versión 3.5 (y diría que también hasta la versión 4.0, ya que hasta el momento no se han introducido modificaciones sobre estos ítems en la versión 4.0 y según parece, tampoco hay intenciones de hacerlo) y comentaremos algunos otros temas relacionados.

Para empezar diremos que la autenticación es el acto de confirmar que algo o alguien es quien dice ser y autorización es el acto de dar permisos (o denegárselos) a algo o alguien (ya autenticado) sobre un recurso, diremos además que algo o alguien es anónimo si no ha sido autenticado, que la acción de loguearse (login) consta de informar las credenciales a fin de poder autenticarse y la acción de desautenticarse (logout), si me permiten el término aunque no suene de lo mejor, consta de volver a un usuario autenticado al estado de anónimo.

En ASP.NET el modo de autenticación por formularios es aplicable para aplicaciones web que requieren autenticación, siempre que la misma no sea autenticación de Windows, para tales casos ASP.NET ofrecerá la opción de establecer el modo de autenticación Windows, pero ese es otro tema.

ASP.NET a través del modo de autenticación por formularios nos brindará mecanismos y funcionalidades que nos ayudarán a realizar ambas tareas: autenticación y autorización.

Para que el modo de autenticación por formularios funcione, solo debemos activarlo y la forma es simplemente ajustar la propiedad mode del tag authentication (del archivo web.config) en Forms, tal como se muestra a continuación:

<authentication mode="Forms">

El modo de autenticación por formularios creará una cookie con la información básica del usuario (obviamente encriptada), la cual será enviada por el browser en cada solicitud y el Servidor analizará la información de esta cookie para hacer los chequeos de autenticación y autorización necesarios. Este modo de funcionamiento es necesario debido a que en un entorno web las conexiones entre el cliente y el servidor se establecen y se cortan en cada solicitud. Por defecto la cookie tomará el nombre “.ASPXAUTH”.

En el siguiente esquema pueden observarse paso a paso las verificaciones de autenticación y autorización que realizará ASP.NET cuando una solicitud es procesada y se está utilizando autenticación por formularios:


Autenticación por formularios

El proceso graficado será realizado automáticamente por ASP.NET con simple hecho de haber establecido el atributo mode como Forms.
En nuestros ejemplos (que los estaremos realizando sobre Visual Studio 2008 SP1) utilizaremos una aplicación web que inicialmente contendrá la página Default.aspx, que es creada automáticamente junto con el proyecto.


Estructura del proyecto

Si en el ejemplo, luego de haber establecido el modo de autenticación por formularios, intentamos acceder a la página Default.aspx, notaremos que podremos hacerlo sin problemas, pero no debemos preocuparnos, este comportamiento se debe a que aún no hemos definido las reglas de acceso, por ahora cualquier usuario podrá acceder a cualquier recurso del sitio.

Para evitar que esto suceda, deberemos configurar las reglas de acceso. Por ahora, para simplificar lo haremos agregando las siguientes líneas dentro de la sección system.web del archivo web.config:

<authorization>
      <deny users="?"/>
</authorization>

Luego veremos en detalle las opciones disponibles para esta sección de la configuración, pero la sintaxis que utilizamos es bastante intuitiva, estaremos indicando que deberá negársele el acceso a cualquier recurso del sitio a los usuarios anónimos (identificados con el signo “?”, recordemos que un usuario anónimo es aquel que no se ha autenticado).

Si luego de esta modificación intentamos acceder nuevamente a Default.aspx (que es un recurso del sitio) veremos que somos automáticamente redireccionados a la página login.aspx, obteniendo un resultado similar al siguiente en la barra de direcciones del explorador:

http://localhost:6931/login.aspx?ReturnUrl….

Se obtendrá un error dado que la página login.aspx no existe, pero ese detalle mínimo pierde importancia ya que como podemos ver, el mecanismo está funcionando.

Lo que está haciendo ASP.NET es verificar la cookie, y al no encontrarla nos ha enviado a un formulario para que ingresemos nuestras credenciales y podamos autenticarnos.

La pagina login.aspx es la página de redirección que utiliza ASP.NET por defecto, pero es posible modificar este comportamiento definiendo un tag denominado forms dentro del tag authentication en el web.config como se muestra a continuación:

<authentication mode="Forms">
      <forms loginUrl="ingreso1.aspx"></forms>
</authentication>

Y no habrá quien se sorprenda de que luego de esta última modificación, la redirección sea a:

http://localhost:6931/ingreso1.aspx?ReturnUrl…….

Existen varias opciones aplicables al tag forms, algunas de ellas, además de loginUrl son:

  • name: Nombre de la cookie (el valor por defecto es .ASPXAUTH).
  • defaultUrl: pagina de redirección si la autenticación fue satisfactoria y el usuario había ingresado originalmente a la página de logueo (login.aspx por defecto).
  • timeout: tiempo de vida de la cookie.
  • slidingExpiration: si es verdadero, el tiempo de vida de la cookie se reiniciará cada vez que la página es reenviada.
  • cookieless: permite definir si se utilizarán cookies para mantener la información del usuario.
  • requireSSL: En verdadero indicará que el browser enviará la cookie al solo si la conexión es segura (SSL) en caso contrario de no contarse con una conexión segura, no funcionará la autenticación por formulario.
  • domain: Permitirá definir en qué dominio la cookie es válida.
    path: Permitirá definir el path de la cookie.

Para continuar con el ejemplo agregaremos la página ingreso1.aspx que será desde ahora en más nuestra página donde el usuario deberá ingresar sus credenciales para autenticarse (login).

Con estas simples modificaciones en el archivo de configuración hemos conseguido dar un primer paso a incorporarle a nuestro sitio la autenticación por formularios. Ahora que hemos conseguido que nadie pueda ingresar, debemos permitir que algunos usuarios si puedan hacerlo, y en particular aquellos a los que deseemos permitirles el ingreso. En este punto encontraremos dos opciones, una de ellas es encargarnos de autenticar al usuario por nuestra cuenta, lo que implica hacer el chequeo manualmente vía código y la otra será una opción automática con algunas particularidades.

Para el primer caso tomará importancia una clase llamada FormsAuthentication. Esta clase (como su nombre indica) nos ayudará a realizar todas las funcionalidades relacionadas con la autenticación por formularios programáticamente, como por ejemplo autenticar a un usuario y redireccionarlo a la pagina que el mismo había solicitado cuando se le denegó el acceso.

Para realizar la autenticación vía código con la ayuda de la clase FormsAuthentication escribiremos lo siguiente:

protected void Login_Click(object sender, EventArgs e)
        {
            if (Usuario.Text == "Juan" && Clave.Text == "clavedejuan")
                FormsAuthentication.RedirectFromLoginPage(Usuario.Text, false);
            else
            {
                LabelError.Text = "Usuario o clave incorrecto";
            }
        }

Hemos asumido que poseemos los cuadros de texto, Usuario, Clave y el botón Login, donde hemos utilizado el evento Click del mismo para agregar el código que puede verse en las líneas superiores. De más está decir que no hemos mencionado a los validadores con el único objetivo de simplificar el ejemplo.

Si probamos el ejemplo intentando ingresar a la página Default.aspx seremos redireccionados a la página Ingreso1.aspx donde deberemos ingresar nuestras credenciales, tal como se muestra a continuación:


Autenticación
Solo podremos acceder a Default.aspx si utilizamos como usuario a “Juan” y como clave a “clavedejuan”.

En el código que terminamos de agregar podemos ver también que nos estamos valiendo del método RedirectFromLoginPage de la clase FormsAuthentication para indicar la autenticación del usuario y la redirección a la página solicitada originalmente por el mismo.

Por otra parte podríamos incluir en la página Default.aspx un botón con la siguiente funcionalidad:

protected void LogOut_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
            FormsAuthentication.RedirectToLoginPage();
        }

Al presionarlo se ha utilizado una vez más la clase FormsAuthentication para desautenticar al usuario y reenviarlo a la página de ingreso nuevamente.

Con estas operaciones hemos conseguido que un usuario pueda realizar las operaciones de autenticación, en tal caso será simple reemplazar la sentencia Usuario.Text == «Juan» && Clave.Text == «clavedejuan» que utilizamos en el ejemplo por la llamada a un método que acceda al repositorio que más deseemos para efectuar la validación de las credenciales del usuario.

Si se observa la clase FormsAuthentication se notará que existe un método llamado Authenticate que toma por parámetros un nombre de usuario y una clave y devuelve un valor booleano, podríamos preguntarnos si este método será capaz de realizar la autenticación por nosotros, esta es la segunda opción que habíamos mencionado en algunos párrafos previos y para probar como funciona, podremos modificar el código anterior de la siguiente forma:

protected void Login_Click(object sender, EventArgs e)
        {
            if (FormsAuthentication.Authenticate(Usuario.Text, Clave.Text))
                FormsAuthentication.RedirectFromLoginPage(Usuario.Text, false);
            else
                LabelError.Text = "Usuario o clave incorrecto";
        }

Si ahora intentamos ingresar nuevamente, ya no importa que usuario y clave utilicemos, no podremos hacerlo, lo cual es un resultado bastante esperable, si consideramos le hemos delegado el control de la autenticación ASP.NET pero aún no le hemos indicado quienes serán los usuarios válidos de la aplicación. Si se desea utilizar esta opción automática debe considerarse la particularidad de que los usuarios deberán definirse en el propio archivo de configuración web.config, como se muestra en el siguiente ejemplo:

<authentication mode="Forms">
  <forms loginUrl="ingreso1.aspx" >
    <credentials passwordFormat="Clear">
      <user name ="Pepe" password ="clavedepepe"/>
      <user name ="Pedro" password ="clavedepedro"/>
    </credentials>
  </forms>
</authentication>

Luego de agregar estos valores veremos que es posible ingresar con ambos usuarios. El atributo passwordFormat del tag credentials admitirá además de la opción Clear (claves en formato limpio, o sea en texto legible para quien abra el archivo web.config) las opciones MD5 y SHA1 que permitirán utilizar el hash de la clave del usuario utilizando los algoritmos MD5 o SHA1. Claro está que en tal caso deberemos crear un mecanismo capaz de establecer dichos valores, lo bueno es que para obtener los valores hash en ambos formatos la clase FormsAuthentication ofrecerá el método HashPasswordForStoringInConfigFile que permitirá tomar una clave limpia y obtener el valor de hash correspondiente en el formato deseado. En el ejemplo anterior, si deseamos utilizar por ejemplo MD5 deberemos efectuar las siguientes modificaciones:

<authentication mode="Forms">
  <forms loginUrl="ingreso1.aspx">
    <credentials passwordFormat="MD5">
      <user name="Pepe" password="957995AA67183A4D2A91F7DE3EB9A692"/>
      <user name="Pedro" password="B447CA7CBA96E91D68D43C5867522BF0"/>
    </credentials>
  </forms>
</authentication>

Donde los valores de hash se han obtenido efectuando las siguientes llamadas:

string HashedPasswordPepe = FormsAuthentication.HashPasswordForStoringInConfigFile("clavedepepe " , "MD5");

y

string HashedPasswordJuan = FormsAuthentication.HashPasswordForStoringInConfigFile("clavedejuan " , "MD5");

Si bien todo esto funciona, creo que es inevitable pensar que se podría dar un próximo paso, ya que al fin de cuentas con este esquema que terminamos de ver, si bien ASP.NET y la clase FormsAuthentication nos han ayudado a controlar la autenticación y la autorización, la funcionalidad de la autenticación debimos codificarla, y cuando utilizamos la autenticación automática con el método Authenticate debimos definir a los usuarios en el archivo web.config. No sería nada malo que ASP.NET pudiese combinar ambas opciones y permitirnos efectuar la autenticación automáticamente sobre cualquier repositorio que deseáramos utilizar.

Evolución

Este modo de funcionamiento que hemos visto, propio de las versiones 1.x del Framework fue mejorado en la versión 2.0 donde se introdujeron cambios que permitieron automatizar ya no solo la autenticación contra cualquier repositorio, sino que también las operaciones (creación, modificación, eliminación, cambios de clave, etc.) de los usuarios involucrados en la misma, lo cual es bastante bueno ya que esas tareas son en realidad también bastante repetitivas.

Todos estos avances pudieron realizarse gracias al esquema de membrecía, más conocido como Membership (En el Framework 2.0 Se ha incluido además de la membrecía, la posibilidad de utilizar roles y perfiles de usuario).

La membrecía además de ofrecer código desarrollado, testeado y mantenido para el manejo de usuarios, brinda la posibilidad de utilizar controles ya incluidos en ASP.NET para realizar una variada cantidad de operaciones y la posibilidad de utilizar como repositorios un motor de base de datos SQL Server, Active Directory o un repositorio personalizado (donde deberá obviamente incluirse el código necesario para su funcionamiento). El siguiente esquema muestra a los actores involucrados y como los mismos se relacionan.


Niveles del Membership
En el esquema puede observarse la existencia de los niveles: repositorios, proveedores, API de membrecía y controles incluidos en ASP.NET relacionados con la membrecía.

El primer nivel (repositorios) hace referencia al lugar donde la información de membrecía será almacenada. El segundo nivel (proveedores) está compuesto por clases dentro del Framework con la capacidad de establecer un enlace entre los repositorios del nivel inferior y la API del nivel superior.

El tercer nivel estará compuesto por la API de membrecía y el último nivel constituido por controles ya existentes en ASP.NET capaces de realizar las operaciones de membrecía y autenticación más frecuentes.
Bajo este esquema de funcionamiento la API en el tercer nivel podrá utilizar cualquier proveedor sin importar su implementación ya que todos los proveedores heredarán una clase en común llamada ProviderBase, brindándole a la API los miembros necesarios para que la misma pueda operar unívocamente con cualquiera de ellos.

Para el caso de SQL Server y Active Directory las implementaciones ya se encontrarán desarrolladas mientras que para realizar otras implementaciones será necesario crear una clase que herede de ProviderBase y se deberán sobreescribir los métodos necesarios apropiadamente.

En nuestro ejemplo utilizaremos como repositorio un motor SQL Server 2008 Enterprise Edition y como la implementación del proveedor SQL Server ya se encuentra desarrollada, simplemente deberemos preparar el repositorio y establecer el proveedor para que la API de membrecía lo utilice.

Antes que nada limpiaremos las líneas que habíamos agregado en el archivo web.config y dejaremos solo las siguientes:

<authentication mode="Forms">
  <forms loginUrl="ingreso1.aspx"/>
</authentication>
<authorization>
  <deny users="?"/>
</authorization>

El primer paso, como mencionamos previamente es el de preparar el repositorio, y considerando que estamos utilizando la opción de SQL Server, esto significará crear la base de datos, tablas, procedimientos almacenados, funciones, etc. para cada uno de los servicios que utilizararemos, para ser consistentes con la terminología utilizada, denominaremos a la membrecía como un servicio que instalaremos sobre el repositorio, (aunque en nuestro caso serán un conjunto de componentes de bases de datos). Además del servicio de membrecía existen otros servicios que también pueden instalarse, como indicaremos a continuación.

Un detalle es que en el caso de SQL Server Express 2005, el proveedor (SqlMembershipProvider) será capaz de instalar el servicio de membrecía automáticamente, pero en nuestro caso deberemos efectuar la tarea manualmente, aunque no es muy desalentador ya que la misma podrá efectuarse ejecutando por línea de comandos un comando llamado aspnet_regsql ajustando sus parámetros según nuestras necesidades. A continuación se describen los parámetros disponibles:

  • -S : Nombre del servidor/instancia SQL Server que deberá ser de la versión 7.0 o superior
  • -U: nombre de usuario
  • -P:Clave del usuario
  • -E: indicará específicamente que se utilizará autenticación integrada vía Windows, por lo que se omitirán los parámetros U y P. Cabe considerarse que si los parámetros U y P no son establecidos el comando asumirá automáticamente la autenticación vía Windows.
  • -C: permitirá establecer el string de conexión ya sea por OLEDB u ODBC
  • -sqlexportonly: Creará los scripts pero no los ejecutará.
  • -A: Indicará que servicios deberán agregarse al repositorio, los cuales son membrecía, roles, perfiles, personalización para páginas con web parts y proveedor de eventos.Es posible instalar todos los servicios a la vez utilizando la opción all o cada servicio individualmente utilizando las opciones m, r, p, c y w para las opciones de membrecía, roles, perfiles, web parts y eventos respectivamente, en este articulo veremos la opción de membrecía y haremos algunos comentarios sobre la opción de roles.
  • -R: Indica que servicios deberán removerse, en contraposición a la opción A y acepta las mismas opciones (all, m, r, p, c y w)
  • -d: permite definir el nombre de la base de datos donde se instalarán los servicios. Tomándose el nombre aspnetdb por defecto.

Para nuestro ejemplo ejecutaremos el comando de la siguiente forma:


Linea de comandos - Membership
Se ha utilizado la dirección:

<unidad:>\WINDOWS\Microsoft.NET\Framework\v2.0.50727

Ya que la versión 3.0 y 3.5 son en realidad upgrades de la versión 2.0 por lo que el archivo aspnet_regsql no se encontrará en carpetas de versiones posteriores, debido a que no ha sido modificado desde la versión 2.0.

Para la utilización de membrecía será suficiente definir el parámetro –A con el valor “m”, esto indicaría que solo instalaremos los servicios de membrecía. Hemos incluido también los servicios de roles (letra “r”), como comentamos previamente, por que haremos unas referencias adicionales en el artículo.

Si luego ingresamos a nuestro servidor de base de datos veremos que el comando ha creado la base de datos pruebamembership y la estructura que el proveedor SqlMembershipProvider utilizará.
Si el archivo aspnet_regsql es ejecutado sin parámetros, se mostrará un asistente (Wizard) que permitirá configurar los parámetros que hemos visto previamente en una ventana visualmente.

Con el repositorio construido, el segundo paso será configurar a ASP.NET para que utilice el repositorio, esto implica hacer que ASP.NET utilice el proveedor SqlMembershipProvider con la configuración de acceso a la base de datos que hemos creado. Este paso es bastante simple y consta de crear una cadena de conexión y configurar el proveedor. Por simplicidad, he definido la cadena de conexión desde las propiedades del proyecto de la siguiente forma:


ConnectionString Membership

Luego de haberse definido la cadena de conexión deberá configurarse el proveedor para que utilice esa cadena de conexión. Esta tarea se realizará dentro del mismo archivo web.config y constará en definir los tags membership y provider (dentro de system.web).

En nuestro caso utilizaremos la siguiente configuración:

<membership defaultProvider="PruebamembershipProvider">
  <providers>
    <add name="PruebamembershipProvider"  connectionStringName="FAYMS.Properties.Settings.pruebamembership"
        applicationName=" Pruebamembership 1"
        enablePasswordRetrieval="false"
        enablePasswordReset="true"
        requiresQuestionAndAnswer="false"
        requiresUniqueEmail="false"
        passwordFormat="Hashed"
        minRequiredPasswordLength="4"
        minRequiredNonalphanumericCharacters="0"
        type="System.Web.Security.SqlMembershipProvider" />
   </providers>
</membership>

En estas líneas hemos agregado a la membrecía el proveedor que hemos llamado PruebamembershipProvider que es del tipo SqlMembershipProvider y utiliza la conexión FAYMS.Properties.Settings.pruebamembership (que es la conexión que hemos definido desde las propiedades del proyecto previamente).

Para cada proveedor existirá además de los dos parámetros que hemos comentado una gran variedad de parámetros de configuración, que determinarán el comportamiento del mismo, para quien esté interesado en conocer todas las posibilidades, les dejo el siguiente enlace:

Propiedades de SqlMembershipProvider

Una vez realizados estos pasos la membrecía ha quedado terminada y lista para su uso.

Visual Studio ofrecerá incluso una herramienta denominada Web Site Administration Tool o simplemente WAT capaz entre otras cosas de operar con proveedores de membrecía. Si deseamos por ejemplo crear un usuario sobre el proveedor que hemos definido recientemente (PruebamembershipProvider) simplemente deberemos seleccionar la opción ASP.NET Configuration del Menú Project y aparecerá la aplicación WAT como se muestra a continuación:


WAT1
Si seleccionamos la opción Security


WAT2
Podremos por ejemplo crear un nuevo usuario seleccionando la opción Create user, donde un formulario de alta de usuarios se mostrará en pantalla, el comportamiento del mismo dependerá de las opciones parametrizadas en el proveedor. En nuestro caso según los parámetros configurados tomará el siguiente formato:


WAT3
El botón Create User Creará el usuario en el repositorio, como puede verse a continuación:


Membership DB2
Si dentro de la WAT cliqueamos la solapa Provider y la opción Select a different provider for each feature (advanced) veremos que se encuentra el proveedor que hemos definido junto al proveedor por defecto.


WAT4

Nuestro proveedor ha quedado seleccionado ya que de esa forma lo hemos definido en el atributo defaultProvider del tag membership, en cuanto al proveedor ya incluido en ASP.NET que puede verse en la imagen, que no utilizaremos en nuestro ejemplo, podremos removerlo de la siguiente forma:

<membership defaultProvider="PruebamembershipProvider">
  <providers>
    <remove name="AspNetSqlMembershipProvider" />
    <add name="PruebamembershipProvider"
    ……..

Luego de tanto trabajo, aunque sin una sola línea de código, lo mínimo que esperaríamos es poder autenticar a Usuario1 tal como lo habíamos hecho hace unos cuantos párrafos atrás sin el uso de membrecía.
Si en la antigua pantalla de Ingreso1.aspx modificamos el código de siguiente forma:

protected void Login_Click(object sender, EventArgs e)
        {
            if (Membership.ValidateUser(Usuario.Text, Clave.Text))
                FormsAuthentication.RedirectFromLoginPage(Usuario.Text, false);
            else
                LabelError.Text = "Usuario o clave incorrecto";
        }

Y luego intentamos ingresar, veremos que todo funcionará según lo esperado. Como podemos ver, la validación del usuario ahora se efectuará mediante la API de membrecía, mientras que el manejo de la autenticación seguirá bajo el control de la clase FormsAuthentication.

La API de membrecía ofrecerá un amplio conjunto de métodos, además de ValidateUser se encontrarán CreateUser, DeleteUser, FindUsersByEmail, FindUsersByName, GeneratePassword, GetAllUsers, GetNumberOfUsersOnline, GetUser, GetUserNameByEmail y UpdateUser, donde sus nombres definen claramente su funcionalidad. La API de membrecía también ofrecerá unas cuantas propiedades y el evento ValidatingPassword que se disparará cuando un usuario es creado o su clave es modificada o reestablecida (reset).

Un objeto del tipo MembershipUser será devuelto por el método GetUser y mientras que un MembershipUserCollection será devuelto en los métodos FindUsersByEmail, FindUsersByName y GetAllUsers. La clase MembershipUser permitirá obtener información del usuario que representa (como por ejemplo: Nombre de usuario, Email, etc.) a través de sus propiedades. MembershipUser también contendrá métodos para realizar algunas operaciones como por ejemplo restablecer la clave de un usuario (reset) o modificarla.

Vuelvo a insistir en el hecho de que una ventaja muy interesante de la implementación de membrecía es que el uso de herencia y configuraciones en el web.config ha permitido un alto nivel de abstracción, el método ValidateUser de la clase Membership desconoce sobre que repositorio se está trabajando. Este desacople entre la API de membrecía y el manejo de los repositorios ha dado lugar a la creación de controles capaces de efectuar operaciones rutinarias de membrecía y autenticación en forma automática. Todo el esquema de autenticación que habíamos organizado en la página Ingreso1.aspx podrá reemplazarse con un solo control incluido ASP.NET y llamado adecuadamente Login.

Si reemplazamos los controles que habíamos agregado en la página Ingreso1.aspx por lo siguiente:

<asp:Login 
        runat="server" 
        ID="Login1" 
        RememberMeText="Recordar" 
        UserNameLabelText="Usuario" 
        PasswordLabelText="Clave"   
        TitleText="Ingreso a la aplicación" 
        FailureText="Usuario o clave incorrecto"
        LoginButtonText="Ingresar" 
        DisplayRememberMe="true">
</asp:Login>

Obtendremos el mismo resultado con la ventaja de validadores incorporados, la opción de recordar al usuario incluida, código testeado, mantenido y listo para usar. Otros controles incorporados son LoginStatus, LoginView, PasswordRecovery, ChangePassword y el interesante CreateUserWizard cuyos nombres nos dan una idea inequívoca de sus funcionalidades.

Ahora que hemos conseguido hacer andar a la autenticación, posiblemente será un buen momento para retomar un detalle que habíamos dejado sujeto con alfileres, el cual es el tag authorization. En su momento lo definimos de la siguiente forma:

<authorization>
  <deny users="?"/>
</authorization>

Donde indicábamos que los usuarios anónimos debían denegarse. El formato general del tag es el siguiente:

<authorization>
  <[allow|deny] users roles verbs />
</authorization>

Donde los tags allow o deny representarán reglas de acceso( a través de las operaciones de “permitir” o “denegar”) a los recursos del sitio. El atributo users representará a un grupo de usuarios y aceptará cuentas de usuario tanto como los simbolos “?” y “*” representando el símbolo “?” a los usuarios anónimos (no logueados) y “*” a todos los usuarios, de tal forma al especificar:

<deny users="?"/>

Se estaba indicando que se denegara el acceso a los recursos del sitio a usuarios anónimos, bajo estas circunstancias, cuando el browser intentase acceder a la página Default.aspx que es específicamente un recurso dentro de la carpeta principal del sitio, con un usuario aún no autenticado, el acceso sería denegado.

Es claro que en ningún lado especificamos que si debe permitirse el acceso a usuarios autenticados, la forma de especificarlo explícitamente sería la siguiente:

<authorization>
  <deny users="?"/>
  <allow users="*"/>
</authorization>

Esto implica en primer lugar que es posible utilizar varios tag allow o deny como reglas de acceso, en segundo lugar que son procesados en orden, o sea la regla deny se aplica primero, y para quienes hayan superado la regla luego se aplicará la regla allow (si invirtiéramos las reglas veríamos que cualquier usuario podría ingresar al sitio dejando a la regla deny posterior sin sentido) y en tercero y último lugar, esto implica que existe un allow * implícito (solo es necesario acceder a

<unidad:>\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\web.config

para confirmarlo, comentario aparte, en este archivo se movieron algunas cuantas definiciones existentes en el archivo machine.config de las versiones 1.x), por ese motivo si eliminamos el tag authorization cualquier usuario podrá ingresar y también podremos omitir el tag allow que escribimos explícitamente en el último ejemplo.
De forma similar podríamos haber utilizado la regla:

<authorization>
  <allow users="Usuario1"/>
  <deny users="*"/>
</authorization>

Si agregamos nuevos usuarios, además de Usuario1 que ya existía previamente e intentamos ingresar a Default.aspx veremos que solo Usuario1 puede hacerlo, los intentos con otros usuarios fallarán, y esto es lo que hemos especificado en las reglas de autorización, primero permitir el acceso al recurso pedido para Usuario1, y de quienes no cumplan la regla primera, ejecutar la segunda regla, que indica que para todos ellos, negar el acceso al recurso. Es posible incluir a varios usuarios separados por coma en el atributo users y evitarnos escribir unos cuantos tags.

Las reglas de autorización regirán para la carpeta del sitio tanto como para las subcarpetas del mismo, supongamos por ejemplo que nuestro sitio posee una subcarpeta de imágenes como se muestra en el ejemplo:


Estructura del proyecto 2

Y en nuestra página de login, Ingreso1.aspx agregamos lo siguiente:

<asp:Image runat="server" ImageUrl="Images/Logo.png" />

Si intentamos navegar la página Default.aspx seremos redireccionados a la página de login (esto se ha vuelto tan repetitivo que ya no debería sorprendernos) pero lo interesante es que veremos que la imagen aparece como inexistente en el browser aunque es claro que si existe.


Autenticación 2
Lo que sucede es que la imagen es también un recurso del sitio y la subcarpeta Images ha heredado las reglas de la carpeta padre, donde se establecía denegar el acceso a los usuarios no autenticados. Quien lo dude podrá simplemente probar autenticarse y luego volver a la página Ingreso1.aspx (sin desautenticarse) y la imagen aparecerá.
Es posible modificar las propiedades de las subcarpetas utilizando un tag llamado location, para nuestro ejemplo deberíamos escribir lo siguiente:

<location path="Images">
  <system.web>
    <authorization>
      <allow users="*"/>
    </authorization>
  </system.web>
</location>

El tag Location debemos definirlo dentro del tag Configuration y al mismo nivel del tag system.web del sitio y el mismo permitirá sobreescribir cualquier definición. En particular, para nuestro caso hemos sobreescrito la definición de autorización para que todo usuario pueda acceder a los recursos de la carpeta Images.

Además del atributo Users existe otro atributo llamado Verb en el tag authorization, que es opcional y permitirá especificar si la regla se aplicará a alguna acción de las siguientes GET, HEAD y POST. Por defecto el valor es “*” (para las tres acciones).

Finalmente veremos que existe la posibilidad de utilizar un atributo llamado roles (no es posible utilizar los atributos roles y users simultáneamente), la forma de uso es equivalente a los usuarios pero viendo esto podríamos preguntarnos ¿qué es un rol?.

Previamente habíamos comentado que el framework ofrecía además la posibilidad de incluir roles para los usuarios, la cual será seguramente una opción muy usual en escenarios de mundo real, comentaremos brevemente como utilizar roles con autenticación por formularios.

Para incluir roles en el esquema que hemos visto se ha adoptado una filosofía similar a la membrecía. Existirá en lugar de una API de membrecía una API de roles y un proveedor de roles, para nuestro caso, donde el repositorio era un servidor SQL Server 2008 Enterprise edition, en la creación del mismo, con la ayuda del comando aspnet_regsql hemos incluido los servicios de roles (incluyendo en el parámetro “-A” el valor “r”), así que solamente deberemos definir el proveedor de roles en el archivo web.config de la siguiente forma:

<roleManager enabled="true" defaultProvider="PruebaRolProvider">
  <providers>
    <remove name="AspNetSqlRoleProvider" />
    <add connectionStringName="FAYMS.Properties.Settings.pruebamembership"
          applicationName="PruebaRol1" name="PruebaRolProvider" type="System.Web.Security.SqlRoleProvider" />
  </providers>
</roleManager>

Desde la WAT encontraremos una sección para el manejo de roles e inclusión de usuarios en los mismos. La API de roles por otra parte nos permitirá realizar varias tareas programáticamente. La ventaja de los roles radica en que se simplificarán las tareas de autorización y configuración de permisos, al igual que sucede en el propio sistema operativo.

Después de tanto visto, habrá seguramente quien se haya percatado de un detalle incómodo, ya que por lo visto hasta ahora hay ciertos datos del usuario (como por ejemplo el mail del mismo) que se encuentran incluidos en este esquema, pero es de esperarse que en cualquier escenario real puedan existir otros datos no hayan sido contemplados, y esta situación es inevitable ya que las posibilidades parecen infinitas. Por ejemplo, en algún tipo de aplicación podría requerirse almacenar el peso o la altura del usuario (para dar un ejemplo simple). Los perfiles o Profiles permitirán incluir información personalizada de los usuarios de la aplicación y una característica interesante es que este servicio no está ligado solamente a la autenticación por formularios, aunque como es de esperarse, funcionará perfectamente con ella. Los perfiles son tema bastante extenso del cual espero, podamos escribir algún artículo posteriormente, pero quería al menos comentar su existencia para que quién los necesite pueda desde este punto continuar investigando de qué se trata el tema.

AJAX

Hay un último detalle que no quería dejar fuera del articulo y está relacionado con el uso de javascript, AJAX y autenticación por formularios, ya que imagino que habrá quien esté interesado en realizar la autenticación que hemos visto utilizando este esquema.

Si bien utilizar AJAX para realizar esta tarea no es complejo, lo que es aún mejor es que esta posibilidad ya ha sido considerada bajo lo que se ha denominado como Servicio de autenticación AJAX de ASP.NET (o ASP.NET AJAX Authentication Service).

Para utilizar el servicio de autenticación AJAX deberemos en primera instancia activar y configurar el servicio web de autenticación y luego utilizarlo desde el cliente mediante javascript y las librerías de AJAX de ASP.NET.

Para establecer el servicio web deberemos simplemente agregar en el archivo de configuración web.config (al mismo nivel del tag system.web) lo siguiente:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL ="false" />
    </webServices>
  </scripting>
</system.web.extensions>

Luego utilizaremos las librerías de AJAX desde javascript para acceder al servicio web, para lo que deberemos incluir el control ScriptManager en cada página que deba acceder al servicio de autenticación desde javascript (de más está decir que existen formas de evitar el tener que agregar un control ScriptManager en cada página que deba acceder al servicio, pero lo explicaremos de esta forma para mantener el ejemplo lo más simple posible).

Nuestra intención en este punto será crear las versiones AJAX de autenticación por formularios para la autenticación (login) y desautenticacion (logout) tal como lo habíamos hecho previamente desde el servidor utilizando las clases Membership y FormsAuthentication.

Como comentamos previamente ASP.NET nos brindará una serie de funciones javascript que nos ayudarán a realizar estas tareas, para el caso del servicio de autenticación AJAX utilizaremos el namespace Sys.Services.AuthenticationService, donde encontraremos entre otras cosas, una función llamada login y otra llamada logout.

Crearemos nuevamente los cuadros de texto para el ingreso de las credenciales del usuario y un botón “Ingresar” de la siguiente forma:

<body>
  <form ….>
    <asp:ScriptManager ID="ScriptManager1" runat="server"/>
    <table>
        …..
                Usuario:
                <asp:TextBox runat="server" ID="txtUsuario" />
 	….		
                Clave:
                <asp:TextBox runat="server" ID="txtClave" TextMode="Password" />
 	….	
                <input type="button" onclick="Autenticar();" value="Aceptar" />
 	….
    </table>

Y luego definimos el siguiente código javascript:

function FuncLoginCompleted(EsAutenticado, Contexto, Metodo) {
            if (EsAutenticado) {                Sys.Services.AuthenticationService.get_defaultUserContext();
            }
            else {
                alert("Usuario o clave incorrecta");
            }
        }
        function FuncFailed(Error, Contexto, Metodo) {
            alert("Se ha producido un error: ");
        }
        function Autenticar() {
            Sys.Services.AuthenticationService.login($get("txtUsuario").value, $get("txtClave").value, false, null, "Default.aspx", FuncLoginCompleted, FuncFailed, null);
        }

Veremos que la autenticación funciona correctamente. Si observamos la función login notaremos que posee una amplia variedad de parámetros tales como:

  • userName: Nombre del usuario a autenticar
  • password: Clave del usuario a autenticar
  • isPersistent: Indicará si la autenticación persistirá luego de cerrarse la sesión. (Equivalente a la opción RememberMe existente en el control login de ASP.NET).
  • redirectUrl: Pagina de redirección en caso que la autenticación haya sido satisfactoria.
  • loginCompletedCallback y failedCallback: Estos parámetros existen debido a que las operaciones contra servicios web son asincrónicas y para este caso puntual, cuando el resultado de la autenticación sea devuelto por el servicio web, la función loginCompletedCallback será llamada. Si ocasionalmente se ha producido algún error durante el proceso se invocará a la función failedCallback.
  • userContext: Aceptará un objeto que será pasado a las funciones loginCompletedCallback o failedCallback cuando las mismas sean invocadas luego que el servicio web haya autenticado al usuario o un error se haya producido.

Por otra parte la función loginCompletedCallback invocada poseerá los siguientes parámetros:

  • validCredentials: Devolverá un valor booleano indicando si la autenticación fue exitosa (lo hemos llamado EsAutenticado en nuestro ejemplo).
  • userContext: Devolverá el objeto que se la ha establecido en el parámetro userContext del la función login (lo hemos llamado Contexto en nuestro ejemplo).
  • methodName: Indicará el nombre del método que se ha llamado (lo hemos llamado Metodo en nuestro ejemplo).

Mientras que la función failedCallback invocada poseerá los parámetros:

  • error: Indicará el error que se ha producido
  • userContext: Devolverá el objeto que se la ha establecido en el parámetro userContext de la función login (lo hemos llamado Contexto en nuestro ejemplo)
  • methodName: Indicará el nombre del método que se ha llamado (lo hemos llamado Metodo en nuestro ejemplo)

Con el ingreso (login) funcionando correctamente podremos redefinir la versión AJAX desautenticacion (logout) de la siguiente manera:

<body>
    <form ….>
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <input type="button" onclick="Salir();" value="Salir" />
    </form>
</body>

Con el siguiente código javascript:

function Salir() {
            Sys.Services.AuthenticationService.logout(null, null, FuncFailed, null);
        }
        function FuncFailed(error, Contexto, Metodo) {
            alert("Se ha producido un error");
        }

La función logout tomará los siguientes parámetros:

  • redirectUrl: Redireccion Pagina de redirección en caso que la salida del sistema haya sido satisfactoria.
  • logoutCompletedCallback y failedCallback: La función logoutCompletedCallback será llamada luego que el servicio web ha sacado al usuario. Si ocasionalmente se ha producido algún error durante el proceso se invocará a la función failedCallback.
  • userContext: Aceptará un objeto que será pasado a las funciones logoutCompletedCallback o failedCallback cuando las mismas sean invocadas luego que el servicio web haya desautenticado al usuario o se haya producido algún error.

Y Finalmente la función logoutCompletedCallback invocada poseerá los siguientes parámetros:

  • result: Sin uso
  • userContext: Devolverá el objeto que se la ha establecido en el parámetro userContext de la función logout
  • methodName: Indicará el nombre del método que se ha llamado

Todas estas funciones forman parte de la extensa librería AJAX de ASP.NET y es posible encontrar toda la documentación de la misma en la dirección:

Librería de Microsoft AJAX

De todo lo visto nos queda como conclusión, que la autenticación por formularios conjuntamente con el uso de membrecía nos permitirá crear código claro, rápido, seguro y configurable para realizar las tareas de autenticación y manejo de usuarios, brindándonos también la posibilidad de incluir roles y e información personalizada en este esquema.

Con este último comentario le doy fin al artículo esperando (después de tanto leído) que lo visto sea de utilidad.

39 comentarios »

  1. epifannio dice:

    Excelente guia funciona perfectamente, un detalle como se puede direccionar a una pagina .aspx específica de acuerdo al tipo de usuario despues de autenticarse?

  2. Dario Krapp dice:

    Muchas gracias Epifannio, el tema es que generalmente un usuario intenta acceder a una página y luego de la autenticación deseará ir a la pagina que había solicitado originalmente, a menos que haya ingresado a la página de login.
    Para hacer que un usuario según alguna condición vaya a una página determinada, la forma que me parece de hacerlo es la siguiente:

                if (Membership.ValidateUser("Usuario1", "Usuario1"))
                {
                    FormsAuthentication.SetAuthCookie("Usuario1", false);
                    if (Roles.IsUserInRole("Usuario1", "Rol1"))
                        Response.Redirect("Default2.aspx");
                    else
                        Response.Redirect("Default3.aspx");
                }

    El usuario y clave están hardcodeados en este ejemplo para simplificarlo. El único punto a mencionar es que debés utilizar el método SetAuthCookie de la clase estática FormsAuthentication para establecer la cookie de autenticación (el segundo parámetro indicará si la cookie será o no persitetnte).
    En este ejemplo si el usuario pertenece al rol «Rol1» es redirigido a la pagina «Default2.aspx» y el caso contrario a «Default3.aspx», pero la condición puede definirse según las necesidades de la aplicación.
    Espero que la respuesta te haya servido.

  3. epifannio dice:

    ok gracias Dario, en un tema que google no me ayudó bastante bien como tú es: Cómo imprimir los datos de un formulario web en asp.net? y mejor si es solo datos por que en los papeles membretados solo hace falta llenar campos vacios al imprimir.

    Te cuento que cuando le doy con «window.print() de javascript» me imprime con cabeceras, pies, botones de la página.

    Dario muchas gracias por tu ayuda..

    • Epiffanio:
      Para elegir que imprimir y que no de una página web puedes utilizar estilos, la clave es crear estilos distintos para lo que no quieras imprimir. Por ejemplo, supongamos que tenemos una página con un cuadro de texto y un botón, y sólo quieres que se imprima el cuadro de texto:

      <INPUT TYPE="text" name="texto" value="Prueba" />
      <INPUT TYPE="submit" value="enviar"/>

      Para que el segundo control no se imprima puedes crear un estilo dentro del medio «print», para que sólo se aplique al imprimir:

      @media print
      {
          .noPrint
          {
              display: none;
          }
      }
      
      BODY
      {
          overflow: hidden;
      .....

      Entonces, nuestro código HTML quedaría así:

      <INPUT TYPE="text" name="texto" value="Prueba" />
      <INPUT TYPE="submit" value="enviar" class="noPrint" />

      Si te fijas en el botón verás que le apliqué el estilo «noPrint» que, en el medio «Print», hace que no se vea. Ten en cuenta que si quieres aplicar varios estilos a un mismo control puedes hacerlo separando los nombres con espacios, por ejemplo: ‘class=»noPrint botonAzul»‘.
      Espero que se entienda la explicación, cualquier otra duda te invito a nuestro foro donde intentaremos responderte a la brevedad: http://foro.scientia.com.ar.
      Suerte!

  4. pueden hacerlo en visual xfis n.n graxias

  5. julio dice:

    Muy buen artículo. Muy bien explicado, me ha sido de mucha ayuda para comenzar en este tema. Saludos!

  6. Oscar Perez dice:

    Muy buena la explicacion.

    Tengo una pregunta: Podria delegar la autorizacion de una aplicacion (www.primer.com) en otra aplicacion (https://validar.net) y seguir trabajando en el primer sitio? Tambien si necesito redirigir a un tercer dominio seguro lo pueda hacer (www.tercero.com) ?

    • Dario Krapp dice:

      Hola Oscar, muchas gracias.

      Lamentablemente no creo que puedas delegar la autenticación en otro sitio con Forms authentication, lo que sucederia es que al loguearte exitosamente en el sitio «validar.net» el mismo va a generar una cookie encriptada en el browser guardando informacion (entre otras cosas) del logueo, esta infomacion es la que chequea el método de consulta de autenticacion que proporciona ASP.NET para consultar si un usuario está autenticado, pero cuando quieras leerlo desde otro sitio, por ejemplo «primer.com» este sitio no va a poder acceder a las cookies del primero, esto esta hecho así por seguridad y es un mecanismo que asegura que no puedas leer informacion de autenticacion de otros sitios desde el tuyo.

      Creo que deberias tomar otro enfoque para lograr esto, una posibilidad es la autenticacion delegada y una opcion es utilizar Open ID, podes encontrar una implementacion en el siguiente link

      http://www.ohloh.net/p/dotnetopenauth/

      Por otra parte Microsoft ya posee una implementacion lista para usar, en este caso Microsoft ofrece toda la plataforma necesaria para realizar la autenticacion delegada y se encarga de guardar las credenciales en servidores propios, podés encontrar información en http://www.passport.net/ .Además de Microsoft hay otras empresas que ofrecen servicios similares.
      Espero que esto te sea de ayuda. Suerte!!!

  7. Adyir dice:

    Hola como estas… Esta muy completo e interesante el post. Desde hace tiempo tengo una duda que no he podido aclarar. Que pasa si yo agarro la cookie de una pc autenticada y la copia a otra pc? Estaria autenticado igual o es valida solo para la maquina donde se creo?
    Es que hay ataques de hackers donde roban las cookies

    • Dario Krapp dice:

      Hola Adyir, es una buena pregunta, nunca he probado hacer esto que comentás, pero en teoría debería funcionar. El tema es que deberías poder tener el acceso a las cookies de una pc autenticada, lo cual seria en sí ya un problema de seguridad.
      Slds

  8. Rigo dice:

    Hola Dario, excelente articulo, solo una pregunta:

    Si deseo autenticar los usuarios registrados en una base de datos, que debo de hacer, ya lo hice mediante variables de Session, pero no me parece que sea lo mejor.

    • Dario Krapp dice:

      Hola Rigo, muchas gracias.
      Creo que es lo mejor evitar el uso de variables de sesión, obviamente dependiendo la cantidad de usuarios concurrentes, la información que se guarde en sesion y como las variables de sesión se utilicen, las mismas pasarán de ser algo poco desable pero sin mayores consecuencias a un problema que impedirá el uso de la aplicacíon, con todas las gamas intermedias entre estos extremos.
      Parte del artículo trata de como autenticar usuarios de una base de datos mediante Forms Authentication, utilizando estas funcionalidades la aplicación no utilizará variables de sesión sino que utilizará cookies encriptadas.

  9. kory23 dice:

    Hola
    Quisiera ver si me puedes dar una idea de cómo extraer las credenciales de windows?.
    Necesito hacer un aplicación para acceder a aplicaciones, pero que valide que si ya estoy logueado con las credenciales de windows no me pida ni usuario ni contraseña.
    Para acceder a dichas aplicaciones lo hago desde SharePoint.

    Saludos!

  10. Paris escort dice:

    ¿Te importa si cito a algunos de sus artículos todo el tiempo que proporcionar crédito y las fuentes de nuevo a su sitio? Mi sitio está en el nicho mismo que el suyo y mis usuarios realmente se beneficiaría de una gran parte de la información que usted proporciona aquí. Por favor , hágamelo saber si esta bien para ti . ¡Gracias! deseo suerte !

  11. Juan Cruz dice:

    Gran comentario se agradece el tema

  12. Juan Cruz dice:

    Buen Tema amigo

  13. Angel Guerrero dice:

    Al momento que agrego la seccion de roles me aparece este error «Las secciones sólo deben aparecer una vez en cada archivo de configuración. Consulte el tema de ayuda sobre las excepciones», modifique cadenas de conexión y nombres pero continua el mensaje, me podrias ayudar un poco

    • Dario Krapp dice:

      Hola Angel,

      Este error que nos comentás es un problema de tag duplicado en el web.config, por ejemplo, la siguiente configuración en el archivo web.config generará un error similar:

      <system.web>
          <authentication mode="Forms">
            <forms loginUrl="~/Account/Login.aspx" timeout="2880"  />
          </authentication>
          <authorization/>
          <authorization>
            <deny users="?"/>
          </authorization>

      Ya que el tag

      <authorization>

      se encuentra duplicado.

      En mi caso, duplicando el tag

      <authorization>

      obtengo el mismo error (aunque se muestra en inglés es el mismo).

      Config section 'system.web/authorization' already defined. Sections must only appear once per config file. See the help topic <location> for exceptions

      Lo te recomiendo es que busques el tag que vas a agregar al archivo de configuración y lo reemplaces en lugar de agregarlo, un tema importante, ahora que veo el mensaje de error, es que al utilizar el tag

      <location>

      pueden duplicarse algunos tags, ya que son configuraciones particulares que se aplican a carpetas específicas del sitio

      Espero que sea de ayuda y suerte

      • Angel Guerrero dice:

        Muchas gracias por la respuesta ya lo puede solucionar, efectivamente como comentas estaba duplicando un tag

  14. Sergio dice:

    Estoy usando la autenticación por formularios y me pasa que la página de login no me carga la masterpage y por lo tanto no me muestra estilos css. Será por algo que estoy omitiendo o siempre pasa así?

    Mi web.config tiene la siguiente configuración:

    y cuando me manda a Default.aspx no me aparecen los estilos. Default.aspx tiene configurado:
    MasterPageFile=»~/MasterPage.master»
    para que me cargue los estilos y otras funciones definidas en MasterPage.master

    • Sergio dice:

      No me salió el codigo del archivo web.config… a ver si ahora sale..

    • Dario Krapp dice:

      Parece ser un tema de permisos.

      Por favor probá ingresar a la aplicacion proporcionando un usuario y clave válidos y luego (sin salir de la aplicacion, o sea sin hacer un sign out) ingresá a la página default.aspx. En este caso los estilos y scripts deberían funcionar.

      Si lo que sucede es esto que describo, te comento que al utilizarse autenticación por formularios todos archivos cumplen las reglas de autorización definidas en el web.config, incluidos los archivos css, scripts, etc.En este caso deberías hacer un ajuste en el web.config para que todo usuario pueda acceder y utilizar estos archivos.

      Por ejemplo, si los estilos se encuentran en la carpeta Styles y se desea que tanto usuarios autenticados o no autenticados puedan descargar (y consecuentemente ver) los estilos, deberías darle autorizacion a esa carpeta a todos los usuarios que ingresen al sitio, una opcion es incluir las siguientes lineas en el web.config (dentro del tag «configuration»):

      <location path="Styles">
          <system.web>
            <authorization>
              <allow users="*" />
            </authorization>
          </system.web>
        </location>

      De esta forma estás especificando que cualquier usuario (autenticado o no) pueda descargar los archivos dentro de la carpeta «Styles». No hay que olvidarse que con estos tags estamos manejando permisos y esta tarea implica responsabilidad, sobre todo dando permisos a cualquier usuario a que descargue archivos del sitio, pero en el caso de css’s o scripts seguramente no va a haber problemas.

      Suerte!

  15. Mario dice:

    hola buenas tardes, estoy desarrollando una aplicacion, pero me encontre con un problema, que por ejemplo me logueo con un usuario A, pero si en esa maquina no cierro la session y ese mismo usuario A entra a otra maquina lo deja entrar, pero ya con una session ya inicializada, como le puedo hacer para cerrar la session anterior o al menos que me diga el propio sistema que ya hay una sesion abierta con ese usuario y que me diga si puedo cerrarla, y si le digo cerrar que me cierre esa sesion y entrar con la k estoy intentando abrir, de lo contrario que no me deje abrir otra sesion.

    espero que me puedan ayudar, gracias

    saludos

  16. Jesus dice:

    Hola, tengo una consulta.

    La autentificación me funciona perfectamente, pero me carga inclusive si mi página de inicio esta configurada para ser un recurso «publico» es decir que la página de inicio puede ser vista sin necesidad de autentificarse.

    Ejemplo:

    Tengo mi ruta: http://www.midominio.com/inicio.aspx y me carga bien, no me pide autentificarme. Oviamente inicio.aspx es mi página de inicio, valga la redundancia.
    Pero si pongo http://www.midominio.com, me pide la autentificación.

    Que puedo hacer en este caso?

    • Hola, Jesus.
      Te hago una consulta: cuál es tu página por defecto? Si no es «inicio.aspx» podés cambiarlo desde el IIS o, en caso que sea un IIS 7.0 o mayor, podés cambiarlo desde el web.config en la siguiente sección:

      <system.webServer>
        <defaultDocument>
          <files>
            <clear />
            <add value="inicio.aspx" />
          </files>
        </defaultDocument>
      </system.webServer>

      En esta página se explica con más detalle cómo cambiar esta configuración: Default Document.
      Suerte!

  17. Leonardo dice:

    Hola, tengo una aplicación hecha con funciona muy bien la primera vez, pero si el usuario cierra la pestaña del explorador e ingresa otro usuario no vuelve a pedir la contraseña. Si cierra el explorador de forma completa si vuelve a pedir una contraseña.
    A que de debe o como lo puedo solucionar? gracias.

    • Hola, Leonardo.
      El tema es que cuando te logeás en un sitio web las credenciales se guardan por instancia del navegador, por eso no te las vuelve a pedir hasta cerrar el browser por completo (a no ser que implementes una opción de logout).
      Saludos.

      • Leonardo dice:

        Hola Gustavo, efectivamente tenemos incorporado la opción logout, pero los estudiantes solo cierran la pestaña y no el explorador, por lo tanto cuando otro estudiante utiliza el equipo accede al login anterior.
        Existe alguna forma de controlar eso o sería mejor buscar otra alternativa para controlar el acceso a paginas restringidas?

        Gracias.

  18. Veronica dice:

    Hola Tengo un inconveniente tengo un formulario de autentificación que verifica por directorio activo se llama Default.aspx, la web.config esta:

    cuando ingreso el usuario y voy a entrar a principal que es donde esta el contenido me sale

    http://localhost/Intranet/Default.aspx?ReturnUrl=

    se supone que ya ingrese el usuario un contraseña correcta, como lo puedo configurar.

  19. scrilexx xiom dice:

    Hola Dario, gracias por la guia es excelente.
    Te hago una consulta supongamos que tengo una base de datos de clientes en sql server
    quiero hacer una pagina de logueo y que el cliente se loguee con su id de cliente y que cdo ingrese vea su deuda, me podrias ayudar como lo tengo q hacer me das algunos tips?

    • Dario Krapp dice:

      Hola, buenos días

      Por un lado deberías implementar autenticación por formularios tal como se explica en el artículo, una vez que tengas la autenticacion por formularios funcionando y puedas loguearte lo que te va a interesar es poder saber qué usuario se encuentra logueado.

      Por el contexto imagino que necesitás hacer esto desde una página .aspx y lo bueno es que la tarea no es muy compleja, ya que la clase System.Web.UI.Page cuenta con la siguiente propiedad:

        public class Page : TemplateControl, IHttpHandler
          {
      
      	....
      	.......
      	............
              //
              // Summary:
              //     Gets information about the user making the page request.
              //
              // Returns:
              //     An System.Security.Principal.IPrincipal that represents the user making the
              //     page request.
              [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
              [Browsable(false)]
              public IPrincipal User { get; }
      

      Por lo que que podrías hacer por ejemplo lo siguiente:

              protected void Page_Load(object sender, EventArgs e)
              {
                  if (this.User.Identity.IsAuthenticated)
                  {
                      string username = this.User.Identity.Name;
      

      Para obtener quién es el usuario logueado y desde ahí en más obtener su id y luego (según lo que me comentás) buscar su deuda en una tabla relacionada con el id de usuario y finalmente mostrarla en la página, o sea:

              protected void Page_Load(object sender, EventArgs e)
              {
                  if (this.User.Identity.IsAuthenticated)
                  {
                      string username = this.User.Identity.Name;
      
                      int uid = ObtenerIdDesdeBD(username); 
      
                      decimal deuda = ObtenerDeudaDesdeDB(uid);
      
                      MostrarDeudaPantalla(deuda); 
      

      Obviamente este es un ejemplo solo para explicar la metodología, en un caso real seguramente guardarás el Id de usuario para reutilizarlo. Por otra parte deberías decidir que hacer si el usuario no se encuentra logueado, para eso puede servirte la sección de «Autorización» que se comenta en el articulo.

      Espero que haya sido de ayuda
      Slds

  20. Gabriel dice:

    Amigo todo excelente, solo que tu estableciste manualmente los usuarios autentificados, y que si se tiene que hacer una consulta dentro de una base de datos SQL server?? un cordial saludo.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.