Validar las credenciales de usuario en el almacén de usuarios de pertenencia (VB)

por Scott Mitchell

Nota:

Desde que se escribió este artículo, los proveedores de pertenencia de ASP.NET han sido reemplazados por ASP.NET Identity. Se recomienda encarecidamente actualizar las aplicaciones para usar la plataforma ASP.NET Identity en lugar de los proveedores de pertenencia destacados en el momento en el que se escribió este artículo. ASP.NET Identity ofrece una serie de ventajas frente al sistema de pertenencia de ASP.NET, incluidas las siguientes:

  • Mejor rendimiento
  • Extensibilidad y capacidad de prueba mejoradas
  • Compatibilidad con OAuth, OpenID Connect y autenticación en dos fases
  • Admite identidades basadas en notificaciones
  • Mejor interoperabilidad con ASP.Net Core

Descargar código o descargar PDF

En este tutorial, examinaremos cómo validar las credenciales de un usuario respecto al almacén de usuarios de pertenencia usando medios de programación y el control de inicio de sesión. También veremos cómo personalizar la apariencia y el comportamiento del control de inicio de sesión.

Introducción

En el tutorial anterior hemos visto cómo crear una nueva cuenta de usuario en el marco de pertenencia. Primero hemos revisado la creación de cuentas de usuario mediante programación a través del método CreateUser de la clase Membership y, a continuación, hemos examinado el uso del control web CreateUserWizard. Sin embargo, actualmente la página de inicio de sesión valida las credenciales proporcionadas respecto a una lista codificada de forma rígida de parejas de nombre de usuario y contraseña. Es necesario actualizar la lógica de la página de inicio de sesión para que valide las credenciales respecto al almacén de usuarios del marco de pertenencia.

Al igual que con la creación de cuentas de usuario, las credenciales se pueden validar mediante programación o mediante declaración. La API de pertenencia incluye un método para validar mediante programación las credenciales de un usuario en el almacén de usuarios. Y ASP.NET incluye el control web de inicio de sesión, lo que representa una interfaz de usuario con cuadros de texto para el nombre de usuario y la contraseña y un botón para iniciar sesión.

En este tutorial, examinaremos cómo validar las credenciales de un usuario respecto al almacén de usuarios de pertenencia usando medios de programación y el control de inicio de sesión. También veremos cómo personalizar la apariencia y el comportamiento del control de inicio de sesión. Comencemos.

Paso 1: Validar las credenciales en el almacén de usuarios de pertenencia

Para los sitios web que usan la autenticación de formularios, un usuario inicia sesión en el sitio web visitando una página de inicio de sesión y escribiendo sus credenciales. A continuación, estas credenciales se comparan con el almacén de usuarios. Si son válidos, al usuario se le concede un vale de autenticación de formularios, que es un token de seguridad que indica la identidad y la autenticidad del visitante.

Para validar un usuario en el marco de pertenencia, use el método ValidateUser de la clase Membership. El método ValidateUser toma dos parámetros de entrada, username y password, y devuelve un valor booleano que indica si las credenciales eran válidas. Al igual que con el método CreateUser que hemos examinado en el tutorial anterior, el método ValidateUser delega la validación real al proveedor de pertenencia configurado.

El SqlMembershipProvider valida las credenciales proporcionadas mediante la obtención de la contraseña del usuario especificada mediante el procedimiento almacenado aspnet_Membership_GetPasswordWithFormat. Recuerde que el SqlMembershipProvider almacena las contraseñas de los usuarios en uno de los tres formatos: borrado, cifrado o con hash. El procedimiento almacenado aspnet_Membership_GetPasswordWithFormat devuelve la contraseña sin formato. Para las contraseñas cifradas o con hash, el SqlMembershipProvider transforma el valor de password pasado al método ValidateUser en su estado equivalente cifrado o con hash y, a continuación, lo compara con lo que se devolvió de la base de datos. Si la contraseña almacenada en la base de datos coincide con la contraseña con formato especificada por el usuario, las credenciales son válidas.

Vamos a actualizar nuestra página de inicio de sesión (~/Login.aspx) para que valide las credenciales proporcionadas respecto al almacén de usuarios del marco de pertenencia. Hemos creado esta página de inicio de sesión en el tutorial Información general sobre la autenticación de formularios, mediante la creación de una interfaz con dos cuadros de texto para el nombre de usuario y la contraseña, una casilla de comprobación "Recordarme" y un botón de inicio de sesión (ver figura 1). El código valida las credenciales ingresadas respecto a una lista codificada de forma rígida de parejas de nombre de usuario y contraseña (Scott/contraseña, Jisun/contraseña y Sam/contraseña).

The Login Page's Interface Includes Two TextBoxes, a CheckBoxList, and a Button

Figura 1: La interfaz de la página de inicio de sesión incluye dos TextBoxes, una CheckBoxList y un Button (haga clic para ver la imagen de tamaño completo)

La interfaz de usuario de la página de inicio de sesión puede permanecer sin cambios, pero es necesario reemplazar el controlador de eventos Click del botón Iniciar sesión por el código que valida al usuario en el almacén de usuarios del marco de pertenencia. Actualice el controlador de eventos para que su código aparezca de la siguiente manera:

Protected Sub LoginButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LoginButton.Click

 ' Validate the user against the Membership framework user store
 If Membership.ValidateUser(UserName.Text, Password.Text) Then
 ' Log the user into the site
 FormsAuthentication.RedirectFromLoginPage(UserName.Text, RememberMe.Checked)
 End If
    
 ' If we reach here, the user's credentials were invalid
 InvalidCredentialsMessage.Visible = True
End Sub

Este código es muy sencillo. Comenzamos llamando al método Membership.ValidateUser y pasando el nombre de usuario y la contraseña proporcionados. Si ese método devuelve true, el usuario ha iniciado sesión en el sitio a través del método RedirectFromLoginPage de la clase FormsAuthentication. (Como hemos explicado en el tutorial Información general sobre la autenticación de formularios, el FormsAuthentication.RedirectFromLoginPage crea el vale de autenticación de formularios y, a continuación, redirige al usuario a la página adecuada). Sin embargo, si las credenciales no son válidas, se muestra la etiqueta InvalidCredentialsMessage, informando al usuario de que su nombre de usuario o contraseña no era correcto.

Eso es todo.

Para probar que la página de inicio de sesión funciona según lo previsto, intente iniciar sesión con una de las cuentas de usuario que creó en el tutorial anterior. O bien, si aún no ha creado una cuenta, hágalo desde la página ~/Membership/CreatingUserAccounts.aspx.

Nota:

Cuando el usuario escribe sus credenciales y envía el formulario de página de inicio de sesión, las credenciales (incluida su contraseña) se transmiten a través de Internet al servidor web en texto sin formato. Esto significa que cualquier hacker que acceda al tráfico de red puede ver el nombre de usuario y la contraseña. Para evitarlo, es esencial que se cifre el tráfico de red mediante Capas de sockets seguros (SSL). Esto garantizará que las credenciales (así como el marcado HTML de toda la página) se cifren desde el momento en que abandonan el navegador web hasta que el servidor web las reciba.

Cómo controla el marco de pertenencia los intentos de inicio de sesión no válidos

Cuando un visitante llega a la página de inicio de sesión y envía sus credenciales, su navegador realiza una solicitud HTTP a la página de inicio de sesión. Si las credenciales son válidas, la respuesta HTTP incluye el vale de autenticación en una cookie. Por lo tanto, un hacker que intenta entrar a su sitio podría crear un programa que envíe exhaustivamente las solicitudes HTTP a la página de inicio de sesión con un nombre de usuario válido y una estimación de la contraseña. Si la estimación de contraseña es correcta, la página de inicio de sesión devolverá la cookie del vale de autenticación, en cuyo momento el programa sabe que ha dado con un par válido de nombre de usuario y contraseña. A través de la fuerza bruta, este programa podría ser capaz de dar con la contraseña de un usuario, especialmente si la contraseña es débil.

Para evitar estos ataques por fuerza bruta, el marco de pertenencia bloquea a un usuario si hay un número determinado de intentos de inicio de sesión erróneos en un período de tiempo determinado. Los parámetros exactos se pueden configurar mediante las dos opciones de configuración del proveedor de pertenencia siguientes:

  • maxInvalidPasswordAttempts: especifica cuántos intentos de contraseña no válidos se permiten para el usuario dentro del período de tiempo antes de que se bloquee la cuenta. El valor predeterminado es 5.
  • passwordAttemptWindow: indica el período de tiempo en minutos durante el cual el número especificado de intentos de inicio de sesión no válidos hará que la cuenta se bloquee. El valor predeterminado es 10.

Si se ha bloqueado a un usuario, no puede iniciar sesión hasta que un administrador desbloquee su cuenta. Cuando un usuario está bloqueado, el método ValidateUsersiempre devuelve False, incluso si se proporcionan credenciales válidas. Aunque este comportamiento reduce la probabilidad de que un hacker acceda a su sitio por métodos de fuerza bruta, puede acabar bloqueando a un usuario válido que simplemente ha olvidado su contraseña o que accidentalmente tiene activado el bloqueo de mayúsculas o está teniendo un mal día tecleando.

Desafortunadamente, no hay ninguna herramienta integrada para desbloquear una cuenta de usuario. Para desbloquear una cuenta, puede modificar la base de datos directamente (cambiar el campo IsLockedOut de la tabla aspnet_Membership de la cuenta de usuario adecuada) o crear una interfaz basada en web que muestre las cuentas bloqueadas con opciones para desbloquearlas. En un próximo tutorial, examinaremos la creación de interfaces administrativas para realizar tareas comunes relacionadas con cuentas de usuario y roles.

Nota:

Una desventaja del método ValidateUser es que, cuando las credenciales proporcionadas no son válidas, no proporciona ninguna explicación sobre por qué. Las credenciales pueden no ser válidas porque no hay ningún par de nombre de usuario y contraseña coincidente en el almacén de usuarios, o porque el usuario aún no ha sido aprobado o porque el usuario ha sido bloqueado. En el paso 4, veremos cómo mostrar un mensaje más detallado al usuario cuando se produzca un error en el intento de inicio de sesión.

Paso 2: Recopilar credenciales a través del control web de inicio de sesión

El control web de inicio de sesión representa una interfaz de usuario predeterminada muy similar a la que hemos creado en el tutorial Información general sobre la autenticación de formularios. El uso del control de inicio de sesión nos ahorra el trabajo de tener que crear la interfaz para recopilar las credenciales del visitante. Además, este control inicia automáticamente la sesión del usuario (suponiendo que las credenciales enviadas sean válidas), lo que nos ahorra tener que escribir código.

Vamos a actualizar Login.aspx, reemplazando la interfaz y el código creados manualmente por un control de inicio de sesión. Para empezar, quite el marcado y el código existentes en Login.aspx. Puede eliminarlos directamente o simplemente volviéndolos un comentario. Para convertir en comentario el marcado declarativo, enciérrelo con los delimitadores <%-- y --%>. Puede escribir estos delimitadores manualmente o, como se muestra en la figura 2, puede seleccionar el texto para convertir en comentario y, a continuación, hacer clic en el icono Comentar las líneas seleccionadas de la barra de herramientas. Del mismo modo, puede usar el icono Comentar las líneas seleccionadas para convertir en comentario el código seleccionado en la clase de código subyacente.

Comment Out the Existing Declarative Markup and Source Code in Login.aspx

Figura 2: Eliminar al convertir en comentario el marcado declarativo y el código fuente existentes en Login.aspx (haga clic para ver la imagen de tamaño completo)

Nota:

El icono Comentar las líneas seleccionadas no está disponible al ver el marcado declarativo en Visual Studio 2005. Si no usa Visual Studio 2008, deberá agregar manualmente los delimitadores <%-- y --%>.

A continuación, arrastre un control de inicio de sesión desde el Cuadro de herramientas a la página y establezca su propiedad ID en myLogin. En este momento, su pantalla debería ser similar a la figura 3. Tenga en cuenta que la interfaz predeterminada del control de inicio de sesión incluye controles TextBox para el nombre de usuario y la contraseña, una casilla de comprobación Recordarme la próxima vez y un botón Iniciar sesión. También hay controles RequiredFieldValidator para los dos cuadros de texto.

Add a Login Control to the Page

Figura 3: Agregar un control de inicio de sesión a la página (haga clic para ver la imagen de tamaño completo)

Ya ha terminado. Cuando se hace clic en el botón Iniciar sesión del control de inicio de sesión, se producirá un postback y el control de inicio de sesión llamará al método Membership.ValidateUser, pasando el nombre de usuario y la contraseña ingresados. Si las credenciales no son válidas, el control de inicio de sesión muestra un mensaje que lo indica. Sin embargo, si las credenciales son válidas, el control de inicio de sesión crea el vale de autenticación de formularios y redirige al usuario a la página adecuada.

El control de inicio de sesión usa cuatro factores para determinar la página adecuada a la cual redirigir al usuario tras un inicio de sesión correcto:

  • Si el control de inicio de sesión está en la página de inicio de sesión tal como se definió en la opción loginUrl de la configuración de autenticación de formularios; el valor predeterminado de esta configuración es Login.aspx
  • La presencia de un parámetro querystring ReturnUrl
  • El valor de la propiedad DestinationUrl del control de inicio de sesión
  • El valor defaultUrl especificado en los ajustes de configuración de autenticación de formularios; el valor predeterminado es Default.aspx.

En la figura 4 se muestra cómo el control de inicio de sesión usa estos cuatro parámetros para llegar a una decisión de página adecuada.

The Login control uses four parameters to arrive at its appropriate page decision.

Figura 4: Agregar un control de inicio de sesión a la página (haga clic para ver la imagen de tamaño completo)

Dedique un momento a probar el control de inicio de sesión visitando el sitio a través de un explorador e iniciando sesión como usuario existente en el marco de pertenencia.

La interfaz representada del control de inicio de sesión es muy configurable. Hay una serie de propiedades que influyen en su apariencia; además, el control de inicio de sesión se puede convertir en una plantilla para un control preciso del diseño de los elementos de la interfaz de usuario. En lo que queda de este paso se examina cómo personalizar la apariencia y el diseño.

Personalización de la apariencia del control de inicio de sesión

La configuración de propiedades predeterminadas del control de inicio de sesión presenta una interfaz de usuario con un título (Inicio de sesión), controles TextBox y Label para las entradas de nombre de usuario y contraseña, una casilla de comprobación Recordarme la próxima vez y un botón Iniciar sesión. La apariencia de estos elementos se pueden configurar a través de las numerosas propiedades del control de inicio de sesión. Además, se pueden agregar elementos adicionales de la interfaz de usuario, como un vínculo a una página para crear una cuenta de usuario, configurando una o dos propiedades.

Dediquemos unos minutos a decorar la apariencia de nuestro control de inicio de sesión. Puesto que la página Login.aspx ya tiene un texto en la parte superior de la página que dice Inicio de sesión, el título del control de inicio de sesión es superfluo. Por lo tanto, borre el valor de la propiedad TitleText para quitar el título del control de inicio de sesión.

Las etiquetas Contraseña: y Nombre de usuario: a la izquierda de los dos controles TextBox se pueden personalizar a través de las propiedades PasswordLabelText y UserNameLabelText, respectivamente. Vamos a cambiar la etiqueta del nombre de usuario para que se lea Nombre de usuario:. Los estilos Label y TextBox se pueden configurar a través de las propiedades LabelStyle y TextBoxStyle, respectivamente.

La propiedad Text del CheckBox Recordarme la próxima vez puede configurarse a través de la propiedad RememberMeText del control de inicio de sesión, y su estado marcado de forma predeterminada se puede configurar mediante la propiedad RememberMeSet (cuyo valor predeterminado es False). Continúe y establezca la propiedad RememberMeSet en True para que la casilla de comprobación Recordarme la próxima vez se active de forma predeterminada.

El control de inicio de sesión ofrece dos propiedades para ajustar el diseño de sus controles de interfaz de usuario. La propiedad TextLayout indica si las etiquetas Nombre de usuario: y Contraseña: aparecen a la izquierda de sus cuadros de texto correspondientes (el valor predeterminado) o encima de ellos. La propiedad Orientation indica si las entradas de nombre de usuario y contraseña se encuentran verticalmente (una por encima de la otra) u horizontalmente. Voy a dejar estas dos propiedades establecidas en sus valores predeterminados, pero le recomendamos que intente establecer estas dos propiedades en sus valores no predeterminados para ver el efecto resultante.

Nota:

En la sección siguiente, Configuración del diseño del control de inicio de sesión, veremos el uso de plantillas para definir el diseño preciso de los elementos de la interfaz de usuario del control Diseño.

Finalice la configuración de las propiedades del control de inicio de sesión estableciendo las propiedades CreateUserUrl y CreateUserText en ¿No se ha registrado todavía? ¡Cree una cuenta! y ~/Membership/CreatingUserAccounts.aspx, respectivamente. Esto agrega un hipervínculo a la interfaz del control de inicio de sesión que apunta a la página que hemos creado en el tutorial anterior. Las propiedades HelpPageUrl y HelpPageText y las propiedades PasswordRecoveryUrl y PasswordRecoveryText del control de inicio de sesión funcionan de la misma manera, representando vínculos hacia una página de ayuda y una página de recuperación de contraseñas.

Después de realizar estos cambios en las propiedades, el marcado declarativo y la apariencia del control de inicio de sesión deberían ser similares a los de la figura 5.

The Login Control's Properties' Values Dictate Its Appearance

Figura 5: Los valores de las propiedades del control de inicio de sesión dictan su apariencia (haga clic para ver la imagen de tamaño completo)

Configuración del diseño del control de inicio de sesión

La interfaz de usuario predeterminada del control web de inicio de sesión establece la interfaz en una <table> HTML. Pero ¿qué ocurre si necesitamos un control más preciso sobre la salida representada? Quizás queremos reemplazar la <table> por una serie de etiquetas <div>. ¿O qué ocurre si nuestra aplicación requiere credenciales adicionales para la autenticación? Muchos sitios web financieros, por ejemplo, requieren que los usuarios proporcionen no solo un nombre de usuario y una contraseña, sino también un número de identificación personal (PIN) u otra información de identificación. Sea cual sea el motivo, es posible convertir el control de inicio de sesión en una plantilla, desde la que podemos definir explícitamente el marcado declarativo de la interfaz.

Tenemos que hacer dos cosas para actualizar el control de inicio de sesión de manera que recopile credenciales adicionales:

  1. Actualizar la interfaz del control de inicio de sesión para incluir controles web que recopilen las credenciales adicionales.
  2. Invalidar la lógica de autenticación interna del control de inicio de sesión para que un usuario solo se autentique si su nombre de usuario y contraseña son válidos y sus credenciales adicionales también son válidas.

Para realizar la primera tarea, es necesario convertir el control de inicio de sesión en una plantilla y agregar los controles web necesarios. En cuanto a la segunda tarea, la lógica de autenticación del control de inicio de sesión se puede reemplazar mediante la creación de un controlador de eventos para el evento Authenticate del control.

Vamos a actualizar el control de inicio de sesión para que solicite a los usuarios su nombre de usuario, contraseña y dirección de correo electrónico y solo autentique al usuario si la dirección de correo electrónico proporcionada coincide con su dirección de correo electrónico en el archivo. En primer lugar, es necesario convertir la interfaz del control de inicio de sesión en una plantilla. En la etiqueta inteligente del control de inicio de sesión, elija la opción Convertir en plantilla.

Convert the Login Control to a Template

Figura 6: Convertir el control de inicio de sesión en una plantilla (haga clic para ver la imagen de tamaño completo)

Nota:

Para revertir el control de inicio de sesión a su versión antes de ser plantilla, haga clic en el vínculo Restablecer desde la etiqueta inteligente del control.

Al convertir el control de inicio de sesión en una plantilla, se agrega una LayoutTemplate al marcado declarativo del control con elementos HTML y controles web que definen la interfaz de usuario. Como se muestra en la figura 7, convertir el control en una plantilla quita una serie de propiedades de la ventana Propiedades, como TitleText, CreateUserUrl, etc., ya que estos valores de propiedad se omiten al usar una plantilla.

Fewer Properties are Available When the Login Control is Converted to a Template

Figura 7: Menos propiedades están disponibles cuando el control de inicio de sesión se convierte en una plantilla (haga clic para ver la imagen de tamaño completo)

El marcado HTML de la LayoutTemplate puede modificarse según sea necesario. Del mismo modo, no dude en agregar nuevos controles web a la plantilla. Sin embargo, es importante que los controles web principales del control de inicio de sesión permanezcan en la plantilla y conserven sus valores ID asignados. En concreto, no quite ni cambie el nombre de los cuadros de texto UserName o Password, la casilla de comprobación RememberMe, el botón LoginButton, la etiqueta FailureText ni los controles RequiredFieldValidator.

Para recopilar la dirección de correo electrónico del visitante, es necesario agregar un TextBox a la plantilla. Agregue el siguiente marcado declarativo entre la fila de tabla (<tr>) que contiene el TextBox Password y la fila de tabla que contiene la casilla de comprobación Recordarme la próxima vez:

<tr>
 <td align="right">
 <asp:Label ID="EmailLabel" runat="server" AssociatedControlID="Email">Email:</asp:Label>
 </td>
 <td>
 <asp:TextBox ID="Email" runat="server"></asp:TextBox>
 <asp:RequiredFieldValidator ID="EmailRequired" runat="server"
 ControlToValidate="Email" ErrorMessage="Email is required."
 ToolTip="Email is required." ValidationGroup="myLogin">*</asp:RequiredFieldValidator>
 </td>
</tr>

Después de agregar el TextBox Email, visite la página a través de un explorador. Como se muestra en la figura 8, la interfaz de usuario del control de inicio de sesión ahora incluye un tercer cuadro de texto.

The Login Control Now Includes a Textbox for the User's Email Address

Figura 8: El control de inicio de sesión ahora incluye un cuadro de texto para la dirección de correo electrónico del usuario (haga clic para ver la imagen de tamaño completo)

En este momento, el control de inicio de sesión sigue usando el método Membership.ValidateUser para validar las credenciales proporcionadas. En consecuencia, el valor especificado en el TextBox Email no tiene ningún efecto sobre si el usuario puede iniciar sesión. En el paso 3 veremos cómo invalidar la lógica de autenticación del control de inicio de sesión para que las credenciales solo se consideren válidas si el nombre de usuario y la contraseña son válidos y la dirección de correo electrónico proporcionada coincide con la dirección de correo electrónico en el archivo.

Paso 3: Modificar la lógica de autenticación del control de inicio de sesión

Cuando un visitante proporciona sus credenciales y hace clic en el botón Iniciar sesión, se produce un postback y el control de inicio de sesión avanza a través de su flujo de trabajo de autenticación. El flujo de trabajo se inicia mediante la generación del evento LoggingIn. Cualquier controlador de eventos asociado a este evento puede cancelar la operación de inicio de sesión estableciendo la propiedad e.Cancel en True.

Si no se cancela la operación de inicio de sesión, el flujo de trabajo avanza generando el evento Authenticate. Si hay un controlador de eventos para el evento Authenticate, este es responsable de determinar si las credenciales proporcionadas son válidas o no. Si no se especifica ningún controlador de eventos, el control de inicio de sesión usa el método Membership.ValidateUser para determinar la validez de las credenciales.

Si las credenciales proporcionadas son válidas, se crea el vale de autenticación de formularios, se genera el evento LoggedIn y se redirige al usuario a la página adecuada. Sin embargo, si las credenciales se consideran no válidas, se genera el evento LoginError y se muestra un mensaje que informa al usuario de que sus credenciales no son válidas. De forma predeterminada, si se produce un error, el control de inicio de sesión simplemente establece la propiedad Text del control de la etiqueta FailureText con un mensaje de error (El intento de inicio de sesión no se realizó correctamente. Vuelva a intentarlo). Sin embargo, si la propiedad FailureAction del control de inicio de sesión está establecida en RedirectToLoginPage, el control de inicio de sesión emite un Response.Redirect a la página de inicio de sesión anexando el parámetro querystring loginfailure=1 (lo que hace que el control de inicio de sesión muestre el mensaje de error).

En la figura 9 se muestra un diagrama de flujo del flujo de trabajo de la autenticación.

The Login Control's Authentication Workflow

Figura 9: Flujo de trabajo de la autenticación del control de inicio de sesión (haga clic para ver la imagen de tamaño completo)

Nota:

Si se pregunta cuándo usaría la opción de página RedirectToLogin de FailureAction, considere el siguiente escenario. En este momento, nuestra página maestra Site.master presenta el texto "Hola, extraño" en la columna izquierda cuando la visita un usuario anónimo, pero imagine que queremos reemplazar ese texto por un control de inicio de sesión. Esto permitiría a un usuario anónimo iniciar sesión desde cualquier página del sitio, en lugar de pedirle que visite la página de inicio de sesión directamente. Sin embargo, si un usuario no pudo iniciar sesión a través del control de inicio de sesión representado por la página maestra, puede que tenga sentido redirigirlo a la página de inicio de sesión (Login.aspx), porque es probable que esa página incluya instrucciones adicionales, vínculos y otra ayuda, como vínculos para crear una nueva cuenta o recuperar una contraseña perdida, que no se agregaron a la página maestra.

Creación del controlador de eventos Authenticate

Para conectar nuestra lógica de autenticación personalizada, es necesario crear un controlador de eventos para el evento Authenticate del control de inicio de sesión. La creación de un controlador de eventos para el evento Authenticate generará la siguiente definición del controlador de eventos:

Protected Sub myLogin_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles myLogin.Authenticate

End Sub

Como puede ver, al controlador de eventos Authenticate se le pasa un objeto de tipo AuthenticateEventArgs como su segundo parámetro de entrada. La clase AuthenticateEventArgs contiene una propiedad booleana denominada Authenticated que se usa para especificar si las credenciales proporcionadas son válidas. Nuestra tarea, a continuación, es escribir aquí un código que determine si las credenciales proporcionadas son válidas o no, y establecer la propiedad e.Authenticate en consecuencia.

Determinación y validación de las credenciales proporcionadas

Use las propiedades Password y UserName del control de inicio de sesión para determinar las credenciales de nombre de usuario y contraseña especificadas por el usuario. Para determinar los valores especificados en cualquier control web adicional (como el TextBox Email que hemos agregado en el paso anterior), use LoginControlID.FindControl("controlID") y obtendrá una referencia de programación al control web de la plantilla cuya propiedad ID es igual a controlID. Por ejemplo, para obtener una referencia al TextBox Email, use el código siguiente:

Dim EmailTextBox As TextBox = CType(myLogin.FindControl("Email"), TextBox)

Para validar las credenciales del usuario, es necesario hacer dos cosas:

  1. Asegúrese de que el nombre de usuario y la contraseña proporcionados sean válidos
  2. Asegúrese de que la dirección de correo electrónico introducida coincide con la dirección de correo electrónico registrada en el archivo para el usuario que intenta iniciar sesión

Para realizar la primera comprobación, simplemente podemos usar el método Membership.ValidateUser como vimos en el paso 1. Para la segunda comprobación, es necesario determinar la dirección de correo electrónico del usuario de manera que se pueda comparar con la dirección de correo electrónico que escribió en el control TextBox. Para obtener información sobre un usuario determinado, use el método GetUser de la clase Membership.

El método GetUser tiene una serie de sobrecargas. Si se usa sin pasar ningún parámetro, devuelve información sobre el usuario que ha iniciado sesión actualmente. Para obtener información sobre un usuario determinado, llame a GetUser pasando su nombre de usuario. En cualquier caso, GetUser devuelve un objeto MembershipUser, que tiene propiedades como UserName, Email, IsApproved, IsOnline, etc.

El código siguiente implementa estas dos comprobaciones. Si ambas pasan, se establece e.Authenticate en True; de lo contrario, se le asigna False.

Protected Sub myLogin_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles myLogin.Authenticate
    
 ' Get the email address entered
 Dim EmailTextBox As TextBox = CType(myLogin.FindControl("Email"), TextBox)
 Dim email As String = EmailTextBox.Text.Trim()
    
 ' Verify that the username/password pair is valid
 If Membership.ValidateUser(myLogin.UserName, myLogin.Password) Then

 ' Username/password are valid, check email
 Dim usrInfo As MembershipUser = Membership.GetUser(myLogin.UserName)
    
 If usrInfo IsNot Nothing AndAlso String.Compare(usrInfo.Email, email, True) = 0 Then
 ' Email matches, the credentials are valid
 e.Authenticated = True
 Else
 ' Email address is invalid...
 e.Authenticated = False
 End If
 Else
 ' Username/password are not valid...
 e.Authenticated = False
 End If
End Sub

Con este código introducido, intente iniciar sesión como un usuario válido, escribiendo el nombre de usuario, la contraseña y la dirección de correo electrónico correctos. Inténtelo de nuevo, pero esta vez use intencionadamente una dirección de correo electrónico incorrecta (ver figura 10). Por último, inténtelo una tercera vez con un nombre de usuario que no exista. En el primer caso, debería iniciar sesión correctamente en el sitio, pero en los dos últimos casos debería ver el mensaje de credenciales no válidas del control de inicio de sesión.

Tito Cannot Log In When Supplying an Incorrect Email Address

Figura 10: Tito no puede iniciar sesión al proporcionar una dirección de correo electrónico incorrecta (haga clic para ver la imagen de tamaño completo)

Nota:

Como se describe en la sección Cómo controla el marco de pertenencia los intentos de inicio de sesión no válidos del paso 1, cuando se llama al método Membership.ValidateUser y se pasan credenciales no válidas, se realiza un seguimiento del intento de inicio de sesión no válido y se bloquea al usuario si supera un umbral determinado de intentos no válidos dentro de un período de tiempo especificado. Dado que nuestra lógica de autenticación personalizada llama al método ValidateUser, una contraseña incorrecta para un nombre de usuario válido incrementará el contador de intentos de inicio de sesión no válidos; sin embargo, este contador no se incrementa en el caso de que el nombre de usuario y la contraseña sean válidos, pero la dirección de correo electrónico sea incorrecta. Es probable que este comportamiento sea adecuado, ya que es poco común que un hacker conozca el nombre de usuario y la contraseña, pero tenga que usar técnicas de fuerza bruta para determinar la dirección de correo electrónico del usuario.

Paso 4: Mejorar el mensaje de credenciales no válidas del control de inicio de sesión

Cuando un usuario intenta iniciar sesión con credenciales no válidas, el control de inicio de sesión muestra un mensaje que explica que el intento de inicio de sesión no terminó correctamente. En concreto, el control muestra el mensaje especificado por su propiedad FailureText, cuyo valor predeterminado es El intento de inicio de sesión no se realizó correctamente. Inténtelo de nuevo.

Recuerde que hay muchas razones por las que las credenciales de un usuario pueden no ser válidas:

  • Es posible que el nombre de usuario no exista
  • El nombre de usuario existe, pero la contraseña no es válida
  • El nombre de usuario y la contraseña son válidos, pero el usuario aún no está aprobado
  • El nombre de usuario y la contraseña son válidos, pero el usuario está bloqueado (probablemente por haber superado el número de intentos de inicio de sesión no válidos dentro del período de tiempo especificado)

Y puede haber otras razones al usar la lógica de autenticación personalizada. Por ejemplo, con el código que hemos escrito en el paso 3, el nombre de usuario y la contraseña pueden ser válidos, pero la dirección de correo electrónico puede ser incorrecta.

Independientemente de por qué las credenciales no son válidas, el control de inicio de sesión muestra el mismo mensaje de error. Esta falta de comentarios puede resultar confusa para un usuario cuya cuenta aún no se ha aprobado o que ha sido bloqueado. Sin embargo, con un poco de trabajo, podemos hacer que el control de inicio de sesión muestre un mensaje más adecuado.

Cada vez que un usuario intenta iniciar sesión con credenciales no válidas, el control de inicio de sesión genera su evento LoginError. Continúe el proceso y cree un controlador de eventos para este evento y, además, agregue el código siguiente:

Protected Sub myLogin_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles myLogin.LoginError
    
 ' Determine why the user could not login...
 myLogin.FailureText = "Your login attempt was not successful. Please try again."
    
 ' Does there exist a User account for this user?
 Dim usrInfo As MembershipUser = Membership.GetUser(myLogin.UserName)

 If usrInfo IsNot Nothing Then
 ' Is this user locked out?
 If usrInfo.IsLockedOut Then
 myLogin.FailureText = "Your account has been locked out because of too many invalid login attempts. Please contact the administrator to have your account unlocked."
 ElseIf Not usrInfo.IsApproved Then
 myLogin.FailureText = "Your account has not yet been approved. You cannot login until an administrator has approved your account."
 End If
 End If
End Sub

El código anterior comienza estableciendo la propiedad FailureText del control de inicio de sesión en el valor predeterminado (El intento de inicio de sesión no se realizó correctamente. Vuelva a intentarlo). A continuación, comprueba si el nombre de usuario proporcionado está asignado a una cuenta de usuario existente. De ser así, consulta las propiedades IsLockedOut y IsApproved del objeto MembershipUser resultante para determinar si la cuenta se ha bloqueado o aún no se ha aprobado. En cualquier caso, la propiedad FailureText se actualiza a un valor correspondiente.

Para probar este código, intente iniciar sesión a propósito como un usuario existente, pero use una contraseña incorrecta. Haga esto cinco veces seguidas en un período de tiempo de 10 minutos y la cuenta se bloqueará. Como se muestra en la figura 11, los intentos de inicio de sesión posteriores siempre producirán un error (incluso con la contraseña correcta), pero ahora mostrarán el mensaje más descriptivo "Su cuenta ha sido bloqueada debido a demasiados intentos de inicio de sesión no válidos. Póngase en contacto con el administrador para desbloquearla".

Tito Performed Too Many Invalid Login Attempts and Has Been Locked Out

Figura 11: Tito realizó demasiados intentos de inicio de sesión no válidos y se ha bloqueado (haga clic para ver la imagen de tamaño completo)

Resumen

Antes de este tutorial, nuestra página de inicio de sesión ha validado las credenciales proporcionadas en una lista codificada de forma rígida de pares de nombre de usuario y contraseña. En este tutorial, hemos actualizado la página para validar las credenciales en el marco de pertenencia. En el paso 1, examinamos el uso del método Membership.ValidateUser mediante programación. En el paso 2 reemplazamos la interfaz de usuario y el código creados manualmente por el control de inicio de sesión.

El control de inicio de sesión representa una interfaz de usuario de inicio de sesión estándar y valida automáticamente las credenciales del usuario en el marco de pertenencia. Además, en caso de que las credenciales sean válidas, el control de inicio de sesión inicia la sesión del usuario a través de la autenticación de formularios. En resumen, una experiencia de usuario de inicio de sesión totalmente funcional está disponible simplemente arrastrando el control de inicio de sesión a una página, sin necesidad de marcado declarativo o código adicionales. Adicionalmente, el control de inicio de sesión es altamente personalizable, lo que permite un alto grado de control sobre la interfaz de usuario y la lógica de autenticación.

En este momento, los visitantes de nuestro sitio web pueden crear una nueva cuenta de usuario e iniciar sesión en el sitio, pero aún tenemos que revisar la restricción del acceso a las páginas en función del usuario autenticado. Actualmente, cualquier usuario, autenticado o anónimo, puede ver cualquier página en nuestro sitio. Además de controlar el acceso a las páginas de nuestro sitio usuario por usuario, es posible que tengamos determinadas páginas cuya funcionalidad depende del usuario. En el siguiente tutorial se examina cómo limitar el acceso y la funcionalidad interna de la página en función del usuario que haya iniciado sesión.

¡Feliz programación!

Lecturas adicionales

Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:

Acerca del autor

Scott Mitchell, autor de varios libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, formador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Se puede contactar con Scott en mitchell@4guysfromrolla.com o a través de su blog en http://ScottOnWriting.NET.

Agradecimiento especial a

Muchos revisores han evaluado esta serie de tutoriales. Los revisores principales de este tutorial fueron Teresa Murphy y Michael Olivero. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.