Crear cuentas de usuario (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 exploraremos el uso del marco de pertenencia (a través de SqlMembershipProvider) para crear nuevas cuentas de usuario. Veremos cómo crear nuevos usuarios mediante programación y a través del control CreateUserWizard integrado de ASP.NET.

Introducción

En el tutorial anterior se instaló el esquema de servicios de aplicación en una base de datos, que agregó las tablas, vistas y procedimientos almacenados necesarios para SqlMembershipProvider y SqlRoleProvider. Esto creó la infraestructura que necesitaremos para el resto de los tutoriales de esta serie. En este tutorial exploraremos el uso del marco de pertenencia (a través de SqlMembershipProvider) para crear nuevas cuentas de usuario. Veremos cómo crear nuevos usuarios mediante programación y a través del control CreateUserWizard integrado de ASP.NET.

Además de aprender a crear cuentas de usuario, también tendremos que actualizar el sitio web de demostración que creamos en el tutorial Información general sobre la autenticación de formularios. Nuestra aplicación web de demo tiene una página de inicio de sesión que valida las credenciales de los usuarios con pares de nombre de usuario y contraseña codificados de forma rígida. Además, Global.asax incluye código que crea objetos IPrincipal y IIdentity personalizados para usuarios autenticados. Actualizaremos la página de inicio de sesión para validar las credenciales de los usuarios en el marco de pertenencia y quitaremos la lógica de identidad y la entidad de seguridad personalizadas.

Comencemos.

Lista de comprobación de autenticación y pertenencia de formularios

Antes de empezar a trabajar con el marco de pertenencia, vamos a dedicar un momento a revisar los pasos importantes que hemos llevado a cabo para llegar a este punto. Al usar el marco de pertenencia con SqlMembershipProvider en un escenario de autenticación basado en formularios, es necesario realizar los pasos siguientes antes de implementar la funcionalidad de pertenencia en la aplicación web:

  1. Habilitar la autenticación basada en formularios. Como se describe en Información general sobre la autenticación de formularios, la autenticación de formularios se habilita al editar Web.config y establecer el atributo mode del elemento <authentication> a Forms. Con la autenticación de formularios habilitada, cada solicitud entrante se examina para un vale de autenticación de formularios, que, si está presente, identifica al solicitante.
  2. Agregue el esquema de servicios de aplicación a la base de datos adecuada. Cuando se usa SqlMembershipProvider, es necesario instalar el esquema de servicios de aplicación en una base de datos. Normalmente, este esquema se agrega a la misma base de datos que contiene el modelo de datos de la aplicación. En el tutorial Crear el esquema de pertenencia en SQL Server se examina el uso de la herramienta aspnet_regsql.exe para lograrlo.
  3. Personalice la Configuración de la aplicación web para hacer referencia a la base de datos del paso 2. En el tutorial Crear el esquema de pertenencia en SQL Server se muestran dos maneras de configurar la aplicación web para que SqlMembershipProvider use la base de datos seleccionada en el paso 2: modificando el nombre de cadena de conexión de LocalSqlServer o agregando un nuevo proveedor registrado a la lista de proveedores del marco de pertenencia y personalizando ese nuevo proveedor para usar la base de datos del paso 2.

Al compilar una aplicación web que use SqlMembershipProvider y la autenticación basada en formularios, deberá realizar estos tres pasos antes de usar la clase Membership o los controles web de inicio de sesión de ASP.NET. Puesto que ya hemos realizado estos pasos en los tutoriales anteriores, estamos listos para empezar a usar el marco de pertenencia.

Paso 1: Agregar nuevas páginas ASP.NET

En este tutorial y los tres siguientes examinaremos varias funciones y funcionalidades relacionadas con la pertenencia. Necesitaremos una serie de páginas ASP.NET para implementar los temas examinados en estos tutoriales. Vamos a crear esas páginas y, a continuación, un archivo (Web.sitemap)de mapa del sitio.

Empiece por crear una nueva carpeta en el proyecto denominado Membership. A continuación, agregue cinco páginas nuevas ASP.NET a la carpeta Membership y vincule cada página con la página maestra Site.master. Asigne un nombre a las páginas:

  • CreatingUserAccounts.aspx
  • UserBasedAuthorization.aspx
  • EnhancedCreateUserWizard.aspx
  • AdditionalUserInfo.aspx
  • Guestbook.aspx

En este momento, el Explorador de soluciones del proyecto debe ser similar a la captura de pantalla que se muestra en la figura 1.

Five New Pages Have Been Added to the Membership Folder

Figura 1: Se han agregado cinco páginas nuevas a la carpeta Membership (haga clic para ver la imagen de tamaño completo)

Cada página debe, en este momento, tener los dos controles de contenido, uno para cada uno de los ContentPlaceHolders de la página maestra: MainContent y LoginContent.

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server"> 
</asp:Content> 
<asp:Content ID="Content2" ContentPlaceHolderID="LoginContent"
Runat="Server"> 
</asp:Content>

Recuerde que el marcado predeterminado de ContentPlaceHolder LoginContent muestra un vínculo para iniciar sesión o cerrar sesión en el sitio, en función de si el usuario está autenticado. Sin embargo, la presencia del control de contenido Content2 invalida el marcado predeterminado de la página maestra. Como hemos descrito en el tutorial Información general sobre la autenticación de formularios, esto es útil en las páginas en las que no queremos mostrar opciones relacionadas con el inicio de sesión en la columna izquierda.

Sin embargo, para estas cinco páginas queremos mostrar el marcado predeterminado de la página maestra para ContentPlaceHolder LoginContent. Por lo tanto, quite el marcado declarativo del control de contenido Content2. Después de hacerlo, cada uno de los cinco marcados de la página debe contener solo un control de contenido.

Paso 2: Creación del mapa del sitio

Todos los sitios web, salvo los más triviales, deben implementar alguna forma de interfaz de usuario de navegación. La interfaz de usuario de navegación puede ser una lista sencilla de vínculos a las distintas secciones del sitio. Como alternativa, estos vínculos se pueden organizar en menús o vistas de árbol. Como desarrolladores de páginas, la creación de la interfaz de usuario de navegación es solo la mitad del trabajo. También necesitamos algunos medios para definir la estructura lógica del sitio de forma que se pueda mantener y actualizar. A medida que se agregan páginas nuevas o se quitan páginas existentes, queremos poder actualizar un único origen (el mapa del sitio) y hacer que esas modificaciones se reflejen en la interfaz de usuario de navegación del sitio.

Estas dos tareas (definir el mapa del sitio e implementar una interfaz de usuario de navegación basada en el mapa del sitio) son fáciles de lograr gracias al marco de mapa del sitio y a los controles web de navegación agregados en la versión 2.0 de ASP.NET. El marco de mapa del sitio permite que un desarrollador defina un mapa del sitio y, a continuación, acceda a él a través de una API mediante programación (la clase SiteMap). Los controles web de navegación integrados incluyen un control Menu, el control TreeView y el control SiteMapPath.

Al igual que los marcos de pertenencia y roles, el marco de mapa del sitio se crea sobre el modelo de proveedor. El trabajo de la clase de proveedor de mapa del sitio es generar la estructura en memoria utilizada por la clase SiteMap desde un almacén de datos persistente, como un archivo XML o una tabla de base de datos. .NET Framework se incluye con un proveedor de mapa del sitio predeterminado que lee los datos de mapa del sitio de un archivo XML (XmlSiteMapProvider) y este es el proveedor que usaremos en este tutorial. Para ver algunas implementaciones alternativas del proveedor de mapas del sitio, consulte la sección Lecturas adicionales al final de este tutorial.

El proveedor de mapa del sitio predeterminado espera que exista un archivo XML con formato correcto denominado Web.sitemap en el directorio raíz. Dado que usamos este proveedor predeterminado, es necesario agregar este archivo y definir la estructura del mapa del sitio en el formato XML adecuado. Para agregar el archivo, haga clic con el botón derecho en el nombre del proyecto en el Explorador de soluciones y elija Agregar nuevo elemento. En el cuadro de diálogo, opte por agregar un archivo de tipo Mapa del sitio denominado Web.sitemap.

Add a File Named Web.sitemap to the Project's Root Directory

Figura 2: Agregue un archivo denominado Web.sitemap al directorio raíz del proyecto (haga clic para ver la imagen de tamaño completo)

El archivo XML de mapa del sitio define la estructura del sitio web como una jerarquía. Esta relación jerárquica se modela en el archivo XML a través de la herencia de los elementos <siteMapNode>. Web.sitemap debe comenzar con un nodo primario <siteMap> que tenga exactamente un elemento secundario <siteMapNode>. Este elemento de nivel superior <siteMapNode> representa la raíz de la jerarquía y puede tener un número arbitrario de nodos descendientes. Cada elemento <siteMapNode> debe incluir un atributo title y puede incluir atributos url y description opcionalmente, entre otros; cada atributo no vacío url debe ser único.

Escriba el XML siguiente en el archivo Web.sitemap:

<?xml version="1.0" encoding="utf-8" ?> 
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> 
 <siteMapNode url="~/Default.aspx" title="Home"> 
 <siteMapNode title="Membership">
 <siteMapNode url="~/Membership/CreatingUserAccounts.aspx" title="Creating User Accounts" /> 
 <siteMapNode url="~/Membership/UserBasedAuthorization.aspx" title="User-Based Authorization" /> 
 <siteMapNode url="~/Membership/Guestbook.aspx" title="Storing Additional User Information" /> 
 </siteMapNode> 
 </siteMapNode> 
</siteMap>

El marcado de mapa del sitio anterior define la jerarquía que se muestra en la figura 3.

The Site Map Represents a Hierarchical Navigational Structure

Figura 3: El mapa del sitio representa una estructura de navegación jerárquica (haga clic para ver la imagen de tamaño completo)

Paso 3: Actualizar la página maestra para incluir una interfaz de usuario de navegación

ASP.NET incluye una serie de controles web relacionados con la navegación para diseñar una interfaz de usuario. Estos incluyen los controles Menu, TreeView y SiteMapPath. Los controles Menu y TreeView representan la estructura del mapa del sitio en un menú o un árbol, respectivamente, mientras que SiteMapPath muestra una ruta de navegación que presenta el nodo actual que se está visitando, así como sus antecesores. Los datos del mapa del sitio se pueden enlazar a otros controles web de datos mediante SiteMapDataSource y se puede acceder a ellos mediante programación a través de la clase SiteMap.

Dado que una explicación exhaustiva del marco de mapa del sitio y los controles de navegación está fuera del ámbito de esta serie de tutoriales, en lugar de dedicar tiempo a crear nuestra propia interfaz de usuario de navegación, vamos a tomar prestado en su lugar la que se usa en mi serie de tutoriales Trabajar con datos en ASP.NET 2.0, que usa un control Repeater para mostrar una lista con viñetas de dos profundidades de vínculos de navegación, como se muestra en la figura 4.

Para crear esta interfaz, agregue el siguiente marcado declarativo a la columna izquierda de la página maestra Site.master donde reside actualmente el texto "TODO: Menu will go here..." (Por hacer: el menú irá aquí...).

<ul> 
 <li> 
 <asp:HyperLink runat="server" ID="lnkHome" NavigateUrl="~/Default.aspx">Home</asp:HyperLink>
 </li> 
 <asp:Repeater runat="server" ID="menu" DataSourceID="SiteMapDataSource1"> 
 <ItemTemplate>
 <li> 
 <asp:HyperLink ID="lnkMenuItem" runat="server" 
 NavigateUrl='<%# Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink>
 <asp:Repeater ID="submenu" runat="server" DataSource="<%# 
 CType(Container.DataItem, SiteMapNode).ChildNodes %>"> 
 <HeaderTemplate> 
 <ul> 
 </HeaderTemplate> 
 <ItemTemplate>
 <li> 
 <asp:HyperLink ID="lnkMenuItem" 
 runat="server" NavigateUrl='<%#  Eval("Url") %>'><%# Eval("Title") %></asp:HyperLink> 
 </li>
 </ItemTemplate> 
 <FooterTemplate> 
 </ul> 
 </FooterTemplate>
 </asp:Repeater> 
 </li> 
 </ItemTemplate> 
 </asp:Repeater> 
</ul>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" ShowStartingNode="false"/>

El marcado anterior enlaza un control Repeater denominado menu a SiteMapDataSource, que devuelve la jerarquía de mapa del sitio definida en Web.sitemap. Dado que la propiedad ShowStartingNode del control SiteMapDataSource se establece en False, comienza a devolver la jerarquía del mapa del sitio a partir de los descendientes del nodo "Inicio". Repeater muestra cada uno de estos nodos (actualmente solo "Membership") en un elemento <li>. Otro Repeater interno muestra los elementos secundarios del nodo actual en una lista anidada sin ordenar.

En la figura 4 se muestra la salida representada del marcado anterior con la estructura de mapa del sitio que creamos en el paso 2. El Repeater representa el marcado de lista sin ordenar en vainilla; las reglas de hoja de estilo CSS definidas en Styles.css son responsables del diseño estéticamente agradable. Para obtener una descripción más detallada de cómo funciona el marcado anterior, consulte el tutorial Páginas maestras y navegación del sitio.

The Navigational User Interface is Rendered Using Nested Unordered Lists

Figura 4: La interfaz de usuario de navegación se representa mediante listas anidadas no ordenadas (haga clic para ver la imagen de tamaño completo)

Agregar la barra de ruta de navegación

Además de la lista de vínculos de la columna izquierda, también vamos a hacer que cada página muestre una ruta de navegación. Una ruta de navegación es un elemento de interfaz de usuario de navegación que muestra rápidamente a los usuarios su posición actual dentro de la jerarquía del sitio. El control SiteMapPath usa el marco de mapa del sitio para determinar la ubicación de la página actual en el mapa del sitio y, a continuación, muestra una ruta de navegación basada en esta información.

En concreto, agregue un elemento <span> al elemento de encabezado de la página maestra <div> y establezca el atributo class del nuevo elemento <span> en "breadcrumb" (ruta de navegación). (La clase Styles.css contiene una regla para una clase "breadcrumb" (ruta de navegación)). A continuación, agregue un SiteMapPath a este nuevo elemento <span>.

<div id="header"> 
 <span class="title">User Account Tutorials</span><br /> 
 <span class="breadcrumb"> 
 <asp:SiteMapPath ID="SiteMapPath1" runat="server"> 
 </asp:SiteMapPath> 
 </span> 
</div>

En la figura 5 se muestra la salida de SiteMapPath al visitar ~/Membership/CreatingUserAccounts.aspx.

The Breadcrumb Displays the Current Page and its Ancestors in the Site Map

Figura 5: La ruta de navegación muestra la página actual y sus antecesores en el mapa del sitio (haga clic para ver la imagen de tamaño completo)

Paso 4: Quitar la lógica de identidad y la entidad de seguridad personalizadas

Los objetos de entidad de seguridad e identidad personalizados se pueden asociar al usuario autenticado. Para ello, creamos un controlador de eventos en Global.asax para el evento de la aplicación PostAuthenticateRequest, que se desencadena después de que FormsAuthenticationModule autentique al usuario. En este controlador de eventos, reemplazamos los objetos GenericPrincipal y FormsIdentity agregados por FormsAuthenticationModule con los objetos CustomPrincipal y CustomIdentity creados en ese tutorial.

Aunque los objetos de entidad de seguridad e identidad personalizados son útiles en determinados escenarios, en la mayoría de los casos, los objetos GenericPrincipal y FormsIdentity son suficientes. Por lo tanto, creo que valdría la pena volver al comportamiento predeterminado. Realice este cambio quitando o comentando el controlador de eventos PostAuthenticateRequest o eliminando completamente el archivo Global.asax.

Nota:

Una vez que haya comentado o quitado el código de Global.asax, deberá comentar el código en la clase de código subyacente Default.aspx's que convierte la propiedad User.Identity en una instancia CustomIdentity.

Paso 5: Crear un nuevo usuario mediante programación

Para crear una cuenta de usuario a través del marco de pertenencia, use el método CreateUser de la clase Membership. Este método tiene parámetros de entrada para el nombre de usuario, la contraseña y otros campos relacionados con el usuario. En la invocación, delega la creación de la nueva cuenta de usuario al proveedor de pertenencia configurado y, a continuación, devuelve un objeto MembershipUser que representa la cuenta de usuario recién creada.

El método CreateUser tiene cuatro sobrecargas, y cada una acepta un número diferente de parámetros de entrada:

Estas cuatro sobrecargas difieren en la cantidad de información recopilada. La primera sobrecarga, por ejemplo, requiere solo el nombre de usuario y la contraseña de la nueva cuenta de usuario, mientras que la segunda también requiere la dirección de correo electrónico del usuario.

Estas sobrecargas existen porque la información necesaria para crear una nueva cuenta de usuario depende de los valores de configuración del proveedor de pertenencia. En el tutorial Crear el esquema de pertenencia en SQL Server se ha examinado la especificación de los valores de configuración del proveedor de pertenencia en Web.config. La tabla 2 incluía una lista completa de los valores de configuración.

Uno de estos valores de configuración del proveedor de pertenencia que afecta a las sobrecargas CreateUser que se pueden usar es el valor requiresQuestionAndAnswer. Si requiresQuestionAndAnswer se establece en true (valor predeterminado), al crear una nueva cuenta de usuario, debe especificar una pregunta y respuesta de seguridad. Esta información se usa más adelante si el usuario necesita restablecer o cambiar su contraseña. En concreto, en ese momento se muestran la pregunta de seguridad y deben escribir la respuesta correcta para restablecer o cambiar su contraseña. Por lo tanto, si requiresQuestionAndAnswer se establece en true, al llamar a cualquiera de las dos primeras sobrecargas CreateUser, se produce una excepción porque falta la pregunta de seguridad y la respuesta. Dado que la aplicación está configurada actualmente para requerir una pregunta y respuesta de seguridad, es necesario usar una de las dos últimas sobrecargas al crear el usuario mediante programación.

Para ilustrar el uso del método CreateUser, vamos a crear una interfaz de usuario donde solicitamos al usuario su nombre, contraseña, correo electrónico y una respuesta a una pregunta de seguridad predefinida. Abra la página CreatingUserAccounts.aspx en la carpeta Membership y agregue los siguientes controles web al control de contenido:

  • TextBox denominado Username
  • TextBox denominado Password, cuya propiedad TextMode está establecida en Password
  • TextBox denominado Email
  • Etiqueta denominada SecurityQuestion con su propiedad Text desactivada
  • TextBox denominado SecurityAnswer
  • Un botón denominado CreateAccountButton cuya propiedad Text está establecida en "Crear la cuenta de usuario"
  • Control de etiqueta denominado CreateAccountResults con su propiedad Text desactivada

En este momento, la pantalla debe ser similar a la captura de pantalla que se muestra en la figura 6.

Add the Various Web Controls to the CreatingUserAccounts.aspx Page

Figura 6: Agregar los distintos controles web a CreatingUserAccounts.aspx Page (haga clic para ver la imagen de tamaño completo)

La etiqueta SecurityQuestion y el TextBox SecurityAnswer están diseñados para mostrar una pregunta de seguridad predefinida y recopilar la respuesta del usuario. Tenga en cuenta que tanto la pregunta de seguridad como la respuesta se almacenan por usuario, por lo que es posible permitir que cada usuario defina su propia pregunta de seguridad. Sin embargo, para este ejemplo he decidido usar una pregunta de seguridad universal, es decir: "¿Cuál es su color favorito?"

Para implementar esta pregunta de seguridad predefinida, agregue una constante a la clase de código subyacente de la página denominada passwordQuestion, asignándola a la pregunta de seguridad. A continuación, en el controlador de eventos Page_Load, asigne esta constante a la propiedad Text de la etiqueta SecurityQuestion:

Const passwordQuestion As String = "What is your favorite color"
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
 If Not Page.IsPostBack Then 
 SecurityQuestion.Text = passwordQuestion 
 End If 
End Sub

A continuación, cree un controlador de eventos para el evento Click de CreateAccountButton' y agregue el código siguiente:

Protected Sub CreateAccountButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles CreateAccountButton.Click 
 Dim createStatus As MembershipCreateStatus 
 Dim newUser As MembershipUser = _ 
 Membership.CreateUser(Username.Text, Password.Text, _ 
 Email.Text, passwordQuestion, _ 
 SecurityAnswer.Text, True, _ 
 createStatus)
 Select Case createStatus 
 Case MembershipCreateStatus.Success 
 CreateAccountResults.Text = "The user account was successfully created!" 
 Case MembershipCreateStatus.DuplicateUserName 
 CreateAccountResults.Text = "There already exists a user with this username." 
 Case MembershipCreateStatus.DuplicateEmail 
 CreateAccountResults.Text = "There already exists a user with this email address." 
 Case MembershipCreateStatus.InvalidEmail
 CreateAccountResults.Text = "There email address you provided in invalid." 
 Case MembershipCreateStatus.InvalidAnswer 
 CreateAccountResults.Text = "There security answer was invalid." 
 Case MembershipCreateStatus.InvalidPassword 
 CreateAccountResults.Text = "The password you provided is invalid. It must be seven characters long and have at least one non-alphanumeric character." 
 Case Else 
 CreateAccountResults.Text = "There was an unknown error; the user account was NOT created."
 End Select 
End Sub

El controlador de eventos Click comienza definiendo una variable denominada createStatus de tipo MembershipCreateStatus. MembershipCreateStatus es una enumeración que indica el estado de la operación CreateUser. Por ejemplo, si la cuenta de usuario se crea correctamente, la instancia resultante MembershipCreateStatus se establecerá en un valor de Success;; por otro lado, si se produce un error en la operación porque ya existe un usuario con el mismo nombre de usuario, se establecerá en un valor de DuplicateUserName. En la sobrecarga CreateUser que usamos, es necesario pasar una instancia MembershipCreateStatus al método. Este parámetro se establece en el valor adecuado dentro del método CreateUser y podemos examinar su valor después de la llamada de método para determinar si la cuenta de usuario se creó correctamente.

Después de llamar a CreateUser, pasar createStatus, se usa una instrucción Select Case para generar un mensaje adecuado en función del valor asignado a createStatus. La figura 7 muestra la salida cuando se ha creado correctamente un nuevo usuario. Las figuras 8 y 9 muestran la salida cuando no se crea la cuenta de usuario. En la figura 8, el visitante escribió una contraseña de cinco letras, que no cumple los requisitos de seguridad de contraseña descritos en el valor de configuración del proveedor de pertenencia. En la figura 9, el visitante intenta crear una cuenta de usuario con un nombre de usuario existente (el creado en la figura 7).

A New User Account is Successfully Created

Figura 7: Se ha creado correctamente una nueva cuenta de usuario (haga clic para ver la imagen de tamaño completo)

The User Account is Not Created Because the Supplied Password is Too Weak

Figura 8: La cuenta de usuario no se crea porque la contraseña proporcionada es demasiado débil (haga clic para ver la imagen de tamaño completo)

The User Account is Not Created Because the Username is Already in Use

Figura 9: La cuenta de usuario no se crea porque el nombre de usuario ya está en uso (haga clic para ver la imagen de tamaño completo)

Nota:

Es posible que se pregunte cómo determinar el éxito o el error al usar una de las dos primeras sobrecargas CreateUser del método, ninguna de los cuales tiene un parámetro de tipo MembershipCreateStatus. Estas dos primeras sobrecargas producen una excepción MembershipCreateUserException en caso de error, que incluye una propiedad StatusCode de tipo MembershipCreateStatus.

Después de crear algunas cuentas de usuario, compruebe que las cuentas se han creado enumerando el contenido de las tablas aspnet_Users y aspnet_Membership de la base de datos SecurityTutorials.mdf. Como se muestra en la figura 10, he agregado dos usuarios a través de la página CreatingUserAccounts.aspx: Tito y Bruce.

There are Two Users in the Membership User Store: Tito and Bruce

Figura 10: Hay dos usuarios en el Almacén de usuarios de pertenencia: Tito y Bruce (haga clic para ver la imagen de tamaño completo)

Aunque el almacén de usuarios de pertenencia ahora incluye información de cuenta de Bruce y Tito, todavía tenemos que implementar la funcionalidad que permite que Bruce o Tito inicien sesión en el sitio. Actualmente, Login.aspx valida las credenciales del usuario en un conjunto codificado de forma rígida de pares de nombre de usuario y contraseña: no valida las credenciales proporcionadas en el marco de pertenencia. Por ahora, ver las nuevas cuentas de usuario en las tablas aspnet_Users y aspnet_Membership tendrá que ser suficiente. En el siguiente tutorial, Validar las credenciales de usuario en el almacén de usuarios de pertenencia, actualizaremos la página de inicio de sesión para validarla en el almacén de pertenencia.

Nota:

Si no ve ningún usuario en la base de datos SecurityTutorials.mdf, puede deberse a que la aplicación web usa el proveedor de pertenencia predeterminado, AspNetSqlMembershipProvider, que usa la base de datos ASPNETDB.mdf como almacén de usuarios. Para determinar si este es el problema, haga clic en el botón Actualizar del Explorador de soluciones. Si se ha agregado una base de datos denominada ASPNETDB.mdf a la carpeta App_Data, este es el problema. Vuelva al paso 4 del tutorial Crear el esquema de pertenencia en SQL Server para obtener instrucciones sobre cómo configurar correctamente el proveedor de pertenencia.

En la mayoría de los escenarios de creación de cuentas de usuario, al visitante se le presenta alguna interfaz para que introduzca su nombre de usuario, contraseña, correo electrónico y otra información esencial, momento en el que se crea una nueva cuenta. En este paso analizamos la creación de una interfaz de este tipo a mano y, a continuación, vimos cómo usar el método Membership.CreateUser para agregar mediante programación la nueva cuenta de usuario en función de las entradas del usuario. Sin embargo, nuestro código acababa de crear la nueva cuenta de usuario. No realizó ninguna acción de seguimiento, como el inicio de sesión del usuario en el sitio con la cuenta de usuario recién creada o el envío de un correo electrónico de confirmación al usuario. Estos pasos adicionales requerirían código adicional en el controlador de eventos Click de Button.

ASP.NET se incluye con el control CreateUserWizard, que está diseñado para controlar el proceso de creación de cuentas de usuario, desde la representación de una interfaz de usuario para crear nuevas cuentas de usuario, hasta la creación de la cuenta en el marco de pertenencia y la realización de tareas posteriores a la creación de cuentas, como enviar un correo electrónico de confirmación y registrar el usuario recién creado en el sitio. El uso del control CreateUserWizard es tan sencillo como arrastrarlo desde el cuadro de herramientas a una página y, a continuación, establecer algunas propiedades. En la mayoría de los casos, no es necesario escribir una sola línea de código. Exploraremos este control ingenioso en detalle en el paso 6.

Si las cuentas de usuario nuevas solo se crean a través de una página web típica de Crear cuenta, es poco probable que tenga que escribir código que use el método CreateUser, ya que es probable que el control CreateUserWizard satisfaga sus necesidades. Sin embargo, el método CreateUser es útil en escenarios en los que necesita una experiencia de usuario de creación de cuenta altamente personalizada o cuando necesita crear nuevas cuentas de usuario mediante programación a través de una interfaz alternativa. Por ejemplo, puede tener una página que permita al usuario cargar un archivo XML que contenga información de usuario de alguna otra aplicación. La página puede analizar el contenido del archivo XML cargado y crear una cuenta para cada usuario representado en el XML llamando al método CreateUser.

Paso 6: Crear un nuevo usuario con el control CreateUserWizard

ASP.NET se incluye con una serie de controles web de inicio de sesión. Estos controles ayudan en muchos escenarios comunes relacionados con la cuenta de usuario y el inicio de sesión. El control CreateUserWizard es uno de estos controles diseñados para presentar una interfaz de usuario para agregar una nueva cuenta de usuario al marco de pertenencia.

Al igual que muchos de los otros controles web relacionados con el inicio de sesión, CreateUserWizard se puede usar sin escribir una sola línea de código. Proporciona intuitivamente una interfaz de usuario basada en el valor de configuración del proveedor de pertenencia y llama internamente al método CreateUser de la clase Membership después que el usuario escriba la información necesaria y haga clic en el botón "Crear usuario". El control CreateUserWizard es extremadamente personalizable. Hay un host de eventos que se activan durante varias fases del proceso de creación de la cuenta. Podemos crear controladores de eventos, según sea necesario, para insertar lógica personalizada en el flujo de trabajo de creación de la cuenta. Además, la apariencia de CreateUserWizard es muy flexible. Hay una serie de propiedades que definen la apariencia de la interfaz predeterminada. Si es necesario, se puede convertir el control en una plantilla o se pueden agregar "pasos" de registro de usuario adicionales.

Comencemos con un vistazo al uso de la interfaz y el comportamiento predeterminados del control CreateUserWizard. A continuación, exploraremos cómo personalizar la apariencia a través de las propiedades y eventos del control.

Examinar la interfaz y el comportamiento predeterminados de CreateUserWizard

Vuelva a la página CreatingUserAccounts.aspx de la carpeta Membership, cambie al modo de diseño o división y agregue un control CreateUserWizard a la parte superior de la página. El control CreateUserWizard se archiva en la sección Controles de inicio de sesión del cuadro de herramientas. Después de agregar el control, establezca su propiedad ID en RegisterUser. Como se muestra en la captura de pantalla de la figura 11, CreateUserWizard representa una interfaz con cuadros de texto para el nombre de usuario, la contraseña, la dirección de correo electrónico y la pregunta y respuesta de seguridad del nuevo usuario.

The CreateUserWizard Control Renders a Generic Create User Interface

Figura 11: El control CreateUserWizard representa una interfaz de usuario de creación genérica (haga clic para ver la imagen de tamaño completo)

Dediquemos un momento a comparar la interfaz de usuario predeterminada generada por el control CreateUserWizard con la interfaz que creamos en el paso 5. Para empezar, el control CreateUserWizard permite al visitante especificar tanto la pregunta de seguridad como la respuesta, mientras que nuestra interfaz creada manualmente usó una pregunta de seguridad predefinida. La interfaz del control CreateUserWizard también incluye controles de validación, mientras que nosotros aún teníamos que implementar la validación en los campos de formulario de nuestra interfaz. Además, la interfaz de control CreateUserWizard incluye un cuadro de texto "Confirmar contraseña" (junto con CompareValidator para asegurarse de que el texto escrito en los cuadros de texto "Contraseña" y "Comparar contraseña" son iguales).

Lo interesante es que el control CreateUserWizard consulta los valores de configuración del proveedor de pertenencia al representar su interfaz de usuario. Por ejemplo, los cuadros de texto de preguntas y respuestas de seguridad solo se muestran si requiresQuestionAndAnswer se establece en True. Del mismo modo, CreateUserWizard agrega automáticamente un control RegularExpressionValidator para asegurarse de que se cumplen los requisitos de seguridad de contraseña y establece sus propiedades ErrorMessage y ValidationExpression en función de los valores de configuración minRequiredPasswordLength, minRequiredNonalphanumericCharactersy passwordStrengthRegularExpression.

El control CreateUserWizard, como su nombre implica, se deriva del control de asistente. Los controles de asistente están diseñados para proporcionar una interfaz para completar tareas de varios pasos. Un control de asistente puede tener un número arbitrario de WizardSteps, cada uno de los cuales es una plantilla que define los controles HTML y Web para ese paso. El control de asistente muestra inicialmente el primer WizardStep, junto con los controles de navegación que permiten al usuario continuar de un paso al siguiente, o volver a los pasos anteriores.

Como se muestra en el marcado declarativo de la figura 11, la interfaz predeterminada del control CreateUserWizard incluye dos WizardSteps:

  • CreateUserWizardStep ? representa la interfaz para recopilar información para crear la nueva cuenta de usuario. Este es el paso que se muestra en la figura 11.
  • CompleteWizardStep ? representa un mensaje que indica que la cuenta se ha creado correctamente.

El comportamiento y la apariencia de CreateUserWizard se pueden modificar convirtiendo cualquiera de estos pasos en plantillas o agregando sus propios WizardStep. Veremos cómo agregar un elemento WizardStep a la interfaz de registro en el tutorial Almacenar información de usuario adicional.

Veamos el control CreateUserWizard en acción. Visite la página CreatingUserAccounts.aspx a través de un explorador. Para empezar, escriba algunos valores no válidos en la interfaz de CreateUserWizard. Intente escribir una contraseña que no cumpla los requisitos de seguridad de contraseña o deje vacío el cuadro de texto "Nombre de usuario". CreateUserWizard mostrará un mensaje de error adecuado. En la figura 12 se muestra la salida al intentar crear un usuario con una contraseña insuficientemente segura.

The CreateUserWizard Automatically Injects Validation Controls

Figura 12: CreateUserWizard inserta automáticamente controles de validación (haga clic para ver la imagen de tamaño completo)

A continuación, escriba los valores adecuados en CreateUserWizard y haga clic en el botón "Crear usuario". Suponiendo que se han especificado los campos obligatorios y la seguridad de la contraseña es suficiente, CreateUserWizard creará una nueva cuenta de usuario a través del marco de pertenencia y, a continuación, mostrará la interfaz de CompleteWizardStep (vea la figura 13). En segundo plano, CreateUserWizard llama al método Membership.CreateUser, como hicimos en el paso 5.

A New User Account has Successfully Been Created

Figura 13: Se ha creado correctamente una nueva cuenta de usuario (haga clic para ver la imagen de tamaño completo)

Nota:

Como se muestra en la figura 13, la interfaz de CompleteWizardStep incluye un botón Continuar. Sin embargo, en este momento, al hacer clic en él solo se realiza un postback, dejando al visitante en la misma página. En la sección "Personalización del comportamiento y la apariencia de CreateUserWizard a través de sus propiedades", veremos cómo puede hacer que este botón envíe al visitante a Default.aspx (o a alguna otra página).

Después de crear una cuenta de usuario, vuelva a Visual Studio y examine las tablas aspnet_Users y aspnet_Membership como hicimos en la figura 10 para comprobar que la cuenta se haya creado correctamente.

Personalización del comportamiento y la apariencia de CreateUserWizard a través de sus propiedades

CreateUserWizard se puede personalizar de varias maneras, a través de propiedades, WizardStep y controladores de eventos. En esta sección veremos cómo personalizar la apariencia del control a través de sus propiedades. En la sección siguiente se examina la extensión del comportamiento del control a través de controladores de eventos.

Prácticamente todo el texto que se muestra en la interfaz de usuario predeterminada del control CreateUserWizard se puede personalizar a través de su gran cantidad de propiedades. Por ejemplo, las etiquetas "Nombre de usuario", "Contraseña", "Confirmar contraseña", "Correo electrónico", "Pregunta de seguridad" y "Respuesta de seguridad" que se muestran a la izquierda de los cuadros de texto se pueden personalizar mediante las propiedades UserNameLabelText, PasswordLabelText, ConfirmPasswordLabelText, EmailLabelText, QuestionLabelText y AnswerLabelText, respectivamente. Del mismo modo, hay propiedades para especificar el texto de los botones "Crear usuario" y "Continuar" en CreateUserWizardStep y CompleteWizardStep, así como si estos botones se representan como Buttons, LinkButtons o ImageButtons.

Los colores, bordes, fuentes y otros elementos visuales se pueden configurar a través de un host de propiedades de estilo. El propio control CreateUserWizard tiene las propiedades comunes de estilo de control web (BackColor, BorderStyle, CssClass, Font, etc.) y hay una serie de propiedades de estilo para definir la apariencia para determinadas secciones de la interfaz de CreateUserWizard. La propiedad TextBoxStyle, por ejemplo, define los estilos de los cuadros de texto de CreateUserWizardStep, mientras que la propiedad TitleTextStyle define el estilo del título ("Registrarse para obtener una cuenta").

Además de las propiedades relacionadas con la apariencia, hay varias propiedades que afectan al comportamiento del control CreateUserWizard. Si la propiedad DisplayCancelButton se establece en True, muestra un botón Cancelar junto al botón "Crear usuario" (el valor predeterminado es False). Si muestra el botón Cancelar, asegúrese de establecer también la propiedad CancelDestinationPageUrl, que especifica la página a la que se envía al usuario después de hacer clic en Cancelar. Como se indicó en la sección anterior, el botón Continuar de la interfaz de CompleteWizardStep provoca un postback, pero deja al visitante en la misma página. Para enviar al visitante a alguna otra página después de hacer clic en el botón Continuar, solo tiene que especificar la dirección URL en la propiedad ContinueDestinationPageUrl.

Vamos a actualizar el control RegisterUser de CreateUserWizard para mostrar un botón Cancelar y enviar al visitante a Default.aspx cuando se hace clic en los botones Cancelar o Continuar. Para ello, establezca la propiedad DisplayCancelButton en True y las propiedades CancelDestinationPageUrl y ContinueDestinationPageUrl en "~/Default.aspx". En la figura 14 se muestra CreateUserWizard actualizado cuando se ve a través de un explorador.

The CreateUserWizardStep Includes a Cancel Button

Figura 14: CreateUserWizardStep incluye un botón Cancelar (haga clic para ver la imagen de tamaño completo)

Cuando un visitante escribe un nombre de usuario, una contraseña, una dirección de correo electrónico y una pregunta y respuesta de seguridad, y hace clic en "Crear usuario", se crea una nueva cuenta de usuario y el visitante inicia sesión como ese usuario recién creado. Suponiendo que la persona que visita la página está creando una nueva cuenta para sí misma, es probable que este sea el comportamiento deseado. Sin embargo, es posible que desee permitir a los Administradores añadir nuevas cuentas de usuario. Al hacerlo, se crearía la cuenta de usuario, pero el Administrador permanecería conectado como Administrador (y no como la cuenta recién creada). Este comportamiento se puede modificar a través de la propiedad booleana LoginCreatedUser.

Las cuentas de usuario del marco de pertenencia contienen una marca aprobada; los usuarios que no están aprobados no pueden iniciar sesión en el sitio. De forma predeterminada, una cuenta recién creada se marca como aprobada, lo que permite al usuario iniciar sesión en el sitio inmediatamente. Sin embargo, es posible tener nuevas cuentas de usuario marcadas como no aprobadas. Quizás desee que un Administrador apruebe manualmente nuevos usuarios antes de que puedan iniciar sesión; o quizás quiera comprobar que la dirección de correo electrónico especificada en el registro es válida antes de permitir que un usuario inicie sesión. Sea cual sea el caso, puede tener la cuenta de usuario recién creada marcada como no aprobada estableciendo la propiedad DisableCreatedUser del control CreateUserWizard en True (el valor predeterminado es False).

Otras propiedades relacionadas con el comportamiento de la nota incluyen AutoGeneratePassword y MailDefinition. Si la propiedad AutoGeneratePassword se establece en True, CreateUserWizardStep no muestra los cuadros de texto "Contraseña" y "Confirmar contraseña"; en su lugar, la contraseña del usuario recién creada se genera automáticamente mediante el método GeneratePassword de la clase Membership. El método GeneratePassword construye una contraseña de una longitud especificada y con un número suficiente de caracteres no alfanuméricos para satisfacer los requisitos de seguridad de contraseña configurados.

La propiedad MailDefinition es útil si desea enviar un correo electrónico a la dirección de correo electrónico especificada durante el proceso de creación de la cuenta. La propiedad MailDefinition incluye una serie de subpropiedades para definir información sobre el mensaje de correo electrónico creado. Estas subpropiedades incluyen opciones como Subject, Priority, IsBodyHtml, From, CC y BodyFileName. La propiedad BodyFileName apunta a un texto o archivo HTML que contiene el cuerpo del mensaje de correo electrónico. El cuerpo admite dos marcadores de posición predefinidos: <%UserName%> y <%Password%>. Estos marcadores de posición, si están presentes en el archivo BodyFileName, se reemplazarán por el nombre y la contraseña del usuario recién creado.

Nota:

La propiedad MailDefinition del control CreateUserWizard solo especifica detalles sobre el mensaje de correo electrónico que se envía cuando se crea una nueva cuenta. No incluye detalles sobre cómo se envía realmente el mensaje de correo electrónico (es decir, si se usa un servidor SMTP o un directorio MailDrop, cualquier información de autenticación, etc.). Estos detalles de bajo nivel deben definirse en la sección <system.net> de Web.config. Para obtener más información sobre estos valores de configuración y sobre el envío de correo electrónico desde ASP.NET 2.0 en general, consulte las Preguntas frecuentes de SystemNetMail.com y mi artículo Envío de Email en ASP.NET 2.0.

Extensión del comportamiento de CreateUserWizard mediante controladores de eventos

El control CreateUserWizard genera una serie de eventos durante su flujo de trabajo. Por ejemplo, después de que un visitante escriba su nombre de usuario, contraseña y otra información pertinente y haga clic en el botón "Crear usuario", el control CreateUserWizard genera su evento CreatingUser. Si e produce algún problema durante el proceso de creación, se activa el evento CreateUserError; sin embargo, si el usuario se crea correctamente, se genera el evento CreatedUser. Hay eventos de control CreateUserWizard adicionales que se generan, pero estos son los tres más importantes.

En determinados escenarios, es posible que deseemos acceder al flujo de trabajo de CreateUserWizard, lo cual podemos hacer mediante la creación de un controlador de eventos para el evento adecuado. Para ilustrar esto, vamos a mejorar el control RegisterUser de CreateUserWizard para incluir alguna validación personalizada en el nombre de usuario y la contraseña. En concreto, vamos a mejorar nuestro CreateUserWizard para que los nombres de usuario no puedan contener espacios iniciales o finales y el nombre de usuario no puede aparecer en ningún lugar de la contraseña. En resumen, queremos impedir que alguien cree un nombre de usuario como "Scott " o tener una combinación de nombre de usuario y contraseña como "Scott" y "Scott.1234".

Para ello, crearemos un controlador de eventos para que el evento CreatingUser realice nuestras comprobaciones de validación adicionales. Si los datos proporcionados no son válidos, es necesario cancelar el proceso de creación. También es necesario agregar un control web de etiqueta a la página para mostrar un mensaje que explique que el nombre de usuario o la contraseña no son válidos. Para empezar, agregue un control Label debajo del control CreateUserWizard, y establezca la propiedad ID en InvalidUserNameOrPasswordMessage y la propiedad ForeColor en Red. Borre la propiedad Text y establezca las propiedades EnableViewState y Visible en False.

<asp:Label runat="server"" id="InvalidUserNameOrPasswordMessage"
 Visible="false" ForeColor="Red" EnableViewState="false"> 
</asp:Label>

A continuación, cree un controlador de eventos para el evento CreatingUser del control CreateUserWizard. Para crear un controlador de eventos, seleccione el control en el Diseñador y, a continuación, vaya a la ventana Propiedades. Desde allí, haga clic en el icono de rayo y, a continuación, haga doble clic en el evento adecuado para crear el controlador de eventos.

Agregue el código siguiente al controlador de eventos CreatingUser :

Protected Sub RegisterUser_CreatingUser(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.LoginCancelEventArgs) Handles RegisterUser.CreatingUser
 Dim trimmedUserName As String = RegisterUser.UserName.Trim() 
 If RegisterUser.UserName.Length <> trimmedUserName.Length Then 
 ' Show the error message 
 InvalidUserNameOrPasswordMessage.Text = "The username cannot contain leading or trailing spaces." 
 InvalidUserNameOrPasswordMessage.Visible = True 
 ' Cancel the create user workflow 
 e.Cancel = True 
 Else 
 ' Username is valid, make sure that the password does not contain the username 
 If RegisterUser.Password.IndexOf(RegisterUser.UserName, StringComparison.OrdinalIgnoreCase) >= 0 Then 
 ' Show the error message 
 InvalidUserNameOrPasswordMessage.Text = "The username may not appear anywhere in the password." 
 InvalidUserNameOrPasswordMessage.Visible = True 
 ' Cancel the create user workflow 
 e.Cancel = True 
 End If 
 End If 
End Sub

Tenga en cuenta que el nombre de usuario y la contraseña especificados en el control CreateUserWizard están disponibles a través de UserName y las propiedades Password, respectivamente. Usamos estas propiedades en el controlador de eventos anterior para determinar si el nombre de usuario proporcionado contiene espacios iniciales o finales y si el nombre de usuario se encuentra dentro de la contraseña. Si se cumple alguna de estas condiciones, se muestra un mensaje de error en la etiqueta InvalidUserNameOrPasswordMessage y la propiedad e.Cancel del controlador de eventos se establece en True. Si e.Cancel se establece en True, CreateUserWizard cortocircuita su flujo de trabajo, lo que cancela eficazmente el proceso de creación de la cuenta de usuario.

En la figura 15 se muestra una captura de pantalla de CreatingUserAccounts.aspx cuando el usuario escribe un nombre de usuario con espacios iniciales.

Usernames with Leading or Trailing Spaces are not Permitted

Figura 15: Los nombres de usuario con espacios iniciales o finales no están permitidos (haga clic para ver la imagen de tamaño completo)

Nota:

Veremos un ejemplo del uso del evento CreatedUser del control CreateUserWizard en el tutorial Almacenamiento de información de usuario adicional.

Resumen

El método CreateUser de la clase Membership crea una nueva cuenta de usuario en el marco de pertenencia. Para ello, delega la llamada al proveedor de pertenencia configurado. En el caso de SqlMembershipProvider, el método CreateUser agrega un registro a las tablas de base de datos aspnet_Users y aspnet_Membership.

Aunque se pueden crear cuentas de usuario nuevas mediante programación (como vimos en el paso 5), un enfoque más rápido y sencillo es usar el control CreateUserWizard. Este control representa una interfaz de usuario de varios pasos para recopilar información de usuario y crear un nuevo usuario en el marco de pertenencia. En realidad, este control usa el mismo método Membership.CreateUser que se examina en el paso 5, pero el control crea la interfaz de usuario, los controles de validación y responde a los errores de creación de cuentas de usuario sin tener que escribir una lista de código.

En este momento tenemos la funcionalidad necesaria para crear nuevas cuentas de usuario. Sin embargo, la página de inicio de sesión sigue validando con esas credenciales codificadas de forma rígida que se han especificado en el segundo tutorial. En el siguiente tutorial actualizaremos Login.aspx para validar las credenciales proporcionadas por el usuario en el marco de pertenencia.

¡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. El revisor principal de este tutorial ha sido Teresa Murphy. ¿Le interesaría revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.