Compartir a través de


Tutorial: Implementar autenticación y autorización personalizadas

Actualización: noviembre 2007

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 funciones.

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

  1. Cree un nuevo proyecto de aplicación para Windows de Visual Basic. Para obtener más información, vea Cómo: Crear un proyecto de aplicación para Windows.

    El nombre predeterminado del formulario principal es Form1.

  2. En el menú Proyecto, haga clic en Agregar nuevo elemento.

  3. 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.

  4. En el menú Proyecto, haga clic en Agregar nuevo elemento.

  5. Seleccione la plantilla Clase, cambie el nombre a SampleIIdentityy, a continuación, haga clic en Agregar.

  6. En el menú Proyecto, haga clic en Agregar nuevo elemento.

  7. Seleccione la plantilla Clase, cambie el nombre a SampleIPrincipaly, a continuación, haga clic en Agregar.

  8. En el menú Proyecto, haga clic en Propiedades de <NombreAplicación>.

  9. En el Diseñador de proyectos, haga clic en la ficha Aplicación.

  10. Cambie el Modo de autenticación desplegable a Definido para la aplicación.

Para configurar el formulario principal

  1. Cambie a Form1 en el Diseñador de formularios.

  2. Agregue un Botón a Form1 desde el Cuadro de herramientas.

    El nombre predeterminado del botón es Button1.

  3. Cambie el texto del botón a Authenticate.

  4. En el Cuadro de herramientas, agregue una Etiqueta a Form1.

    El nombre predeterminado de la etiqueta es Label1.

  5. Cambie el texto de la etiqueta a una cadena vacía.

  6. En el Cuadro de herramientas, agregue una Etiqueta a Form1.

    El nombre predeterminado de la etiqueta es Label2.

  7. Cambie el texto de la etiqueta a una cadena vacía.

  8. 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.

  9. 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

  1. Seleccione el archivo SampleIIdentity.vb en el Explorador de soluciones.

    Esta clase encapsula la identidad de un usuario.

  2. 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.

  3. 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
    
  4. 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"
    
  5. 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.

  6. La propiedad Name debe devolver el nombre del usuario asociado con esta identidad.

    Escriba el código siguiente en la propiedad Name.

    Return nameValue
    
  7. Cree una propiedad que devuelva la función del usuario.

    Public ReadOnly Property Role() As ApplicationServices.BuiltInRole
        Get
            Return roleValue
        End Get
    End Property
    
  8. 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
    
  9. Cree un método denominado IsValidNameAndPassword que determine si es válida una combinación de nombre de usuario y contraseña.

    Nota de 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
    
  10. Cree funciones denominadas GetHashedPassword y GetSalt que devuelvan la contraseña con código hash y salt del usuario especificado.

    Nota de 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

  1. 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.

  2. 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.

  3. Agregue un campo privado para almacenar la identidad asociada con esta entidad principal.

    Private identityValue As SampleIIdentity
    
  4. 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.

  5. 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
    
  6. 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

  1. Seleccione LoginForm1 en el diseñador.

  2. Haga doble clic en el botón Aceptar para abrir el Editor de código en el evento Click.

  3. 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

  1. Inicie la aplicación.

  2. Haga clic en Autenticar.

    Aparece el formulario de inicio de sesión.

  3. 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.

  4. Haga clic en Aceptar para descartar el cuadro de mensaje.

  5. 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.

  6. Haga clic en Autenticar.

    Aparece el formulario de inicio de sesión.

  7. 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

Conceptos

Acceso a los datos del usuario

Referencia

My.User (Objeto)

IIdentity

IPrincipal

Otros recursos

Autenticación y autorización en .NET Framework con Visual Basic