Tutorial: Implementar autenticación y autorización personalizadas (Visual Basic)
Este tutorial muestra cómo implementar la autenticación y la autorización personalizadas con clases que derivan de IIdentity y IPrincipal. En este tutorial también se muestra cómo reemplazar la identidad predeterminada del subproceso de la aplicación, la identidad de Windows, mediante el establecimiento de My.User.CurrentPrincipal en una instancia de la clase que deriva de IPrincipal. La nueva información de usuario está disponible de forma inmediata mediante el objeto My.User, que devuelve información sobre la identidad del usuario actual del subproceso.
Las aplicaciones empresariales suelen proporcionar acceso a datos o recursos basándose en credenciales proporcionadas por el usuario. Normalmente, dichas aplicaciones comprueban la función de un usuario y proporcionan acceso a recursos basándose en dicha función. Common Language Runtime proporciona compatibilidad para la autorización basada en funciones tomando como base una cuenta de Windows o una identidad personalizada. Para obtener más información, vea Seguridad basada en roles.
Introducción
En primer lugar, prepare un proyecto con un formulario principal y un formulario de inicio de sesión y configúrelo para utilizar la autenticación personalizada.
Para crear la aplicación de ejemplo
Cree un nuevo proyecto de aplicación para Windows de Visual Basic. Para obtener más información, vea Cómo: Crear un nuevo proyecto de aplicación de Windows Forms.
El nombre predeterminado del formulario principal es Form1.
En el menú Proyecto, haga clic en Agregar nuevo elemento.
Seleccione la plantilla Formulario de inicio de sesión y haga clic en Agregar.
El nombre predeterminado del formulario de inicio de sesión es LoginForm1.
En el menú Proyecto, haga clic en Agregar nuevo elemento.
Seleccione la plantilla Clase, cambie el nombre a SampleIIdentityy, a continuación, haga clic en Agregar.
En el menú Proyecto, haga clic en Agregar nuevo elemento.
Seleccione la plantilla Clase, cambie el nombre a SampleIPrincipaly, a continuación, haga clic en Agregar.
En el menú Proyecto, haga clic en Propiedades de <NombreAplicación>.
En el Diseñador de proyectos, haga clic en la ficha Aplicación.
Cambie el Modo de autenticación desplegable a Definido para la aplicación.
Para configurar el formulario principal
Cambie a Form1 en el Diseñador de formularios.
Agregue un Botón a Form1 desde el Cuadro de herramientas.
El nombre predeterminado del botón es Button1.
Cambie el texto del botón a Authenticate.
En el Cuadro de herramientas, agregue una Etiqueta a Form1.
El nombre predeterminado de la etiqueta es Label1.
Cambie el texto de la etiqueta a una cadena vacía.
En el Cuadro de herramientas, agregue una Etiqueta a Form1.
El nombre predeterminado de la etiqueta es Label2.
Cambie el texto de la etiqueta a una cadena vacía.
Haga doble clic en Button1 para crear el controlador de eventos para el evento Click y, a continuación, abra el Editor de código.
Agregue el código siguiente al método Button1_Click.
My.Forms.LoginForm1.ShowDialog() ' Check if the user was authenticated. If My.User.IsAuthenticated Then Me.Label1.Text = "Authenticated " & My.User.Name Else Me.Label1.Text = "User not authenticated" End If If My.User.IsInRole(ApplicationServices.BuiltInRole.Administrator) Then Me.Label2.Text = "User is an Administrator" Else Me.Label2.Text = "User is not an Administrator" End If
Puede ejecutar la aplicación, pero dado que no existe código de autenticación, no autenticará a ningún usuario. En la sección siguiente se explica cómo agregar código de autenticación.
Crear una identidad
.NET Framework utiliza las interfaces IIdentity y IPrincipal como base para la autenticación y la autorización. Su aplicación puede utilizar la autenticación de usuario personalizada mediante la implementación de estas interfaces, como muestran estos procedimientos.
Para crear una clase que implementa IIdentity
Seleccione el archivo SampleIIdentity.vb en el Explorador de soluciones.
Esta clase encapsula la identidad de un usuario.
En la línea que sigue a Public Class SampleIIdentity, agregue el código siguiente para heredar de IIdentity.
Implements System.Security.Principal.IIdentity
Después de agregar este código y presionar ENTRAR, el Editor de código crea propiedades de código auxiliar que debe implementar.
Agregue campos privados para almacenar el nombre de usuario y un valor que indica si se autentica el usuario.
Private nameValue As String Private authenticatedValue As Boolean Private roleValue As ApplicationServices.BuiltInRole
Escriba el código siguiente en la propiedad AuthenticationType.
La propiedad AuthenticationType debe devolver una cadena que indique el mecanismo de autenticación actual.
Este ejemplo utiliza la autenticación especificada explícitamente, por lo que la cadena es "Custom Authentication". Si los datos de autenticación de usuario se han almacenado en una base de datos de SQL Server, el valor podría ser "SqlDatabase".
Return "Custom Authentication"
Escriba el código siguiente en la propiedad IsAuthenticated.
Return authenticatedValue
La propiedad IsAuthenticated debe devolver un valor que indique si se ha autenticado el usuario.
La propiedad Name debe devolver el nombre del usuario asociado con esta identidad.
Escriba el código siguiente en la propiedad Name.
Return nameValue
Cree una propiedad que devuelva la función del usuario.
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole Get Return roleValue End Get End Property
Cree un método Sub New que inicialice la clase mediante la autenticación del usuario y el establecimiento del nombre y la función del usuario, basándose en un nombre y una contraseña.
Este método llama a un método denominado IsValidNameAndPassword para determinar si es válida una combinación de nombre de usuario y contraseña.
Public Sub New(ByVal name As String, ByVal password As String) ' The name is not case sensitive, but the password is. If IsValidNameAndPassword(name, password) Then nameValue = name authenticatedValue = True roleValue = ApplicationServices.BuiltInRole.Administrator Else nameValue = "" authenticatedValue = False roleValue = ApplicationServices.BuiltInRole.Guest End If End Sub
Cree un método denominado IsValidNameAndPassword que determine si es válida una combinación de nombre de usuario y contraseña.
Nota sobre la seguridad El algoritmo de autenticación debe tratar las contraseñas de forma segura. Por ejemplo, la contraseña no se debe almacenar en un campo de clase.
No debe almacenar las contraseñas de usuario en el sistema, ya que si se filtra esta información no contará con una aplicación segura. Podría almacenar el código hash de la contraseña de cada usuario. (Una función hash cifra los datos de forma que la entrada no se puede deducir a partir de la salida.) No se puede determinar una contraseña directamente a partir de su código hash.
Sin embargo, un usuario malintencionado podría dedicar tiempo a generar un diccionario de los códigos hash de todas las contraseñas posibles y, a continuación, consultar la contraseña de un código hash determinado. Para protegerse contra este tipo de ataque, debería agregar un valor salt a la contraseña antes de aplicarle un algoritmo hash para generar así un código hash con valor salt. Los valores salt son datos adicionales únicos a cada contraseña, que impiden calcular con antelación un diccionario de códigos hash.
Para proteger las contraseñas de los usuarios malintencionados, sólo debe almacenar valores hash y salt de las contraseñas, preferiblemente en un equipo seguro. Es muy difícil que un usuario malintencionado pueda recuperar una contraseña a partir de un código hash con valor salt. Este ejemplo utiliza los métodos GetHashedPassword y GetSalt para cargar la contraseña con código hash y salt de un usuario.
Private Function IsValidNameAndPassword( ByVal username As String, ByVal password As String) As Boolean ' Look up the stored hashed password and salt for the username. Dim storedHashedPW As String = GetHashedPassword(username) Dim salt As String = GetSalt(username) 'Create the salted hash. Dim rawSalted As String = salt & Trim(password) Dim saltedPwBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(rawSalted) Dim sha1 As New System.Security.Cryptography. SHA1CryptoServiceProvider Dim hashedPwBytes() As Byte = sha1.ComputeHash(saltedPwBytes) Dim hashedPw As String = Convert.ToBase64String(hashedPwBytes) ' Compare the hashed password with the stored password. Return hashedPw = storedHashedPW End Function
Cree funciones denominadas GetHashedPassword y GetSalt que devuelvan la contraseña con código hash y salt del usuario especificado.
Nota sobre la seguridad Debe evitar codificar las contraseñas con código hash y salt en las aplicaciones cliente por las dos razones. En primer lugar, los usuarios malintencionados pueden tener acceso a ellos y encontrar una colisión hash. En segundo lugar, no puede cambiar ni revocar la contraseña de un usuario. La aplicación debe obtener la contraseña con código hash y salt de un usuario determinado a partir de un origen seguro que mantiene un administrador.
Por razones de simplicidad, en este ejemplo se ha especificado una contraseña con código hash y salt; sin embargo, en código de producción debe utilizar un enfoque más seguro. Por ejemplo, podría almacenar la información sobre el usuario en una base de datos de SQL Server y tener acceso a ella con procedimientos almacenados. Para obtener más información, vea Cómo: Conectarse a los datos de una base de datos.
Nota
La contraseña que corresponde a esta contraseña con código hash especificada se indica en la sección "Probar la aplicación".
Private Function GetHashedPassword(ByVal username As String) As String ' Code that gets the user's hashed password goes here. ' This example uses a hard-coded hashed passcode. ' In general, the hashed passcode should be stored ' outside of the application. If Trim(username).ToLower = "testuser" Then Return "ZFFzgfsGjgtmExzWBRmZI5S4w6o=" Else Return "" End If End Function Private Function GetSalt(ByVal username As String) As String ' Code that gets the user's salt goes here. ' This example uses a hard-coded salt. ' In general, the salt should be stored ' outside of the application. If Trim(username).ToLower = "testuser" Then Return "Should be a different random value for each user" Else Return "" End If End Function
Ahora, el archivo SampleIIdentity.vb debe contener el código siguiente:
Public Class SampleIIdentity
Implements System.Security.Principal.IIdentity
Private nameValue As String
Private authenticatedValue As Boolean
Private roleValue As ApplicationServices.BuiltInRole
Public ReadOnly Property AuthenticationType() As String Implements System.Security.Principal.IIdentity.AuthenticationType
Get
Return "Custom Authentication"
End Get
End Property
Public ReadOnly Property IsAuthenticated() As Boolean Implements System.Security.Principal.IIdentity.IsAuthenticated
Get
Return authenticatedValue
End Get
End Property
Public ReadOnly Property Name() As String Implements System.Security.Principal.IIdentity.Name
Get
Return nameValue
End Get
End Property
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole
Get
Return roleValue
End Get
End Property
Public Sub New(ByVal name As String, ByVal password As String)
' The name is not case sensitive, but the password is.
If IsValidNameAndPassword(name, password) Then
nameValue = name
authenticatedValue = True
roleValue = ApplicationServices.BuiltInRole.Administrator
Else
nameValue = ""
authenticatedValue = False
roleValue = ApplicationServices.BuiltInRole.Guest
End If
End Sub
Private Function IsValidNameAndPassword(
ByVal username As String,
ByVal password As String) As Boolean
' Look up the stored hashed password and salt for the username.
Dim storedHashedPW As String = GetHashedPassword(username)
Dim salt As String = GetSalt(username)
'Create the salted hash.
Dim rawSalted As String = salt & Trim(password)
Dim saltedPwBytes() As Byte =
System.Text.Encoding.Unicode.GetBytes(rawSalted)
Dim sha1 As New System.Security.Cryptography.
SHA1CryptoServiceProvider
Dim hashedPwBytes() As Byte = sha1.ComputeHash(saltedPwBytes)
Dim hashedPw As String = Convert.ToBase64String(hashedPwBytes)
' Compare the hashed password with the stored password.
Return hashedPw = storedHashedPW
End Function
Private Function GetHashedPassword(ByVal username As String) As String
' Code that gets the user's hashed password goes here.
' This example uses a hard-coded hashed passcode.
' In general, the hashed passcode should be stored
' outside of the application.
If Trim(username).ToLower = "testuser" Then
Return "ZFFzgfsGjgtmExzWBRmZI5S4w6o="
Else
Return ""
End If
End Function
Private Function GetSalt(ByVal username As String) As String
' Code that gets the user's salt goes here.
' This example uses a hard-coded salt.
' In general, the salt should be stored
' outside of the application.
If Trim(username).ToLower = "testuser" Then
Return "Should be a different random value for each user"
Else
Return ""
End If
End Function
End Class
Crear una entidad principal
A continuación, debe implementar una clase que derive de IPrincipal y especificar que devuelva instancias de la clase SampleIIdentity.
Para crear una clase que implementa IPrincipal
Seleccione el archivo SampleIPrincipal.vb en el Explorador de soluciones.
Esta clase encapsula la identidad de un usuario. Puede utilizar el objeto My.User para asociar esta entidad principal con el subproceso actual y tener acceso a la identidad del usuario.
En la línea que sigue a Public Class SampleIPrincipal, agregue el código siguiente para heredar de IPrincipal.
Implements System.Security.Principal.IPrincipal
Después de agregar este código y presionar ENTRAR, el Editor de código crea una propiedad y un método de código auxiliar que debe implementar.
Agregue un campo privado para almacenar la identidad asociada con esta entidad principal.
Private identityValue As SampleIIdentity
Escriba el código siguiente en la propiedad Identity.
Return identityValue
La propiedad Identity debe devolver la identidad de usuario de la entidad principal actual.
Escriba el código siguiente en el método IsInRole.
El método IsInRole determina si la entidad principal actual pertenece a la función especificada.
Return role = identityValue.Role.ToString
Cree un método Sub New que inicialice la clase con una nueva instancia de SampleIIdentity dado un nombre de usuario y contraseña.
Public Sub New(ByVal name As String, ByVal password As String) identityValue = New SampleIIdentity(name, password) End Sub
Este código establece la identidad de usuario de la clase SampleIPrincipal.
Ahora, el archivo SampleIPrincipal.vb debe contener el código siguiente:
Public Class SampleIPrincipal
Implements System.Security.Principal.IPrincipal
Private identityValue As SampleIIdentity
Public ReadOnly Property Identity() As System.Security.Principal.IIdentity Implements System.Security.Principal.IPrincipal.Identity
Get
Return identityValue
End Get
End Property
Public Function IsInRole(ByVal role As String) As Boolean Implements System.Security.Principal.IPrincipal.IsInRole
Return role = identityValue.Role.ToString
End Function
Public Sub New(ByVal name As String, ByVal password As String)
identityValue = New SampleIIdentity(name, password)
End Sub
End Class
Conectar el formulario de inicio de sesión
La aplicación puede utilizar el formulario de inicio de sesión para recopilar un nombre de usuario y contraseña. Puede utilizar esta información para inicializar una instancia de la clase SampleIPrincipal y utilizar el objeto My.User para establecer la identidad del subproceso actual en dicha instancia.
Para configurar el formulario de inicio de sesión
Seleccione LoginForm1 en el diseñador.
Haga doble clic en el botón Aceptar para abrir el Editor de código en el evento Click.
Reemplace el código del método OK_Click por el siguiente código.
Dim samplePrincipal As New SampleIPrincipal( Me.UsernameTextBox.Text, Me.PasswordTextBox.Text) Me.PasswordTextBox.Text = "" If (Not samplePrincipal.Identity.IsAuthenticated) Then ' The user is still not validated. MsgBox("The username and password pair is incorrect") Else ' Update the current principal. My.User.CurrentPrincipal = samplePrincipal Me.Close() End If
Probar la aplicación
Ahora que la aplicación tiene el código de autenticación, puede ejecutarla e intentar autenticar un usuario.
Para probar la aplicación
Inicie la aplicación.
Haga clic en Autenticar.
Aparece el formulario de inicio de sesión.
Escriba TestUser en el cuadro Nombre de usuario y BadPassword en el cuadro Password; a continuación, haga clic en Aceptar.
Aparece un cuadro de mensaje que indica que la combinación de nombre de usuario y contraseña no es correcta.
Haga clic en Aceptar para descartar el cuadro de mensaje.
Haga clic en Cancelar para cancelar el formulario de inicio de sesión.
Las etiquetas del formulario principal indican Usuario no autenticado y El usuario no es administrador.
Haga clic en Autenticar.
Aparece el formulario de inicio de sesión.
Escriba TestUser en el cuadro User name y Password en el cuadro Password; a continuación, haga clic en Aceptar. Asegúrese de que la contraseña se escribe con las mayúsculas correctas.
Las etiquetas del formulario principal indican TestUser autenticado y El usuario es administrator.
Vea también
Tareas
Cómo: Conectarse a los datos de una base de datos
Referencia
Conceptos
Acceso a los datos de usuario (Visual Basic)
Otros recursos
Autenticación y autorización en .NET Framework con Visual Basic