Twitter Facebook RSS Feed

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

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

  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.