Procedura dettagliata: implementazione di autenticazione e autorizzazione personalizzate (Visual Basic)

In questa procedura dettagliata viene illustrata l'implementazione di autenticazione e autorizzazione personalizzate mediante l'utilizzo di classi che derivano dalla IIdentity e dalla IPrincipal. In questa procedura dettagliata viene anche illustrato l'override dell'identità predefinita del thread dell'applicazione, l'identità Windows mediante l'impostazione di My.User.CurrentPrincipal su un'istanza della classe che deriva dalla IPrincipal. Le informazioni sul nuovo utente sono immediatamente disponibili mediante l'oggetto My.User, il quale restituisce informazioni sull'identità dell'utente corrente del thread.

Le applicazioni aziendali offrono spesso un accesso a dati o risorse basato su credenziali fornite dall'utente. Queste applicazioni controllano in genere il ruolo di un utente e consentono l'accesso alle risorse sulla base di tale ruolo. Common Language Runtime fornisce il supporto dell'autorizzazione basata sui ruoli tramite account Windows o identità personalizzate. Per ulteriori informazioni, vedere Sicurezza basata sui ruoli.

Introduzione

Creare innanzitutto un progetto con un form principale e un form di accesso e configurarlo in modo da utilizzare l'autenticazione personalizzata.

Per creare l'applicazione di esempio

  1. Creare un nuovo progetto Applicazione Windows di Visual Basic. Per ulteriori informazioni, vedere Procedura: creare un progetto applicazione Windows.

    Il nome predefinito del form principale è Form1.

  2. Scegliere Aggiungi nuovo elemento dal menu Progetto.

  3. Selezionare il modello Form di accesso e fare clic su Aggiungi.

    Il nome predefinito del form di accesso è LoginForm1.

  4. Scegliere Aggiungi nuovo elemento dal menu Progetto.

  5. Selezionare il modello Classe, modificare il nome in SampleIIdentity, quindi fare clic su Aggiungi.

  6. Scegliere Aggiungi nuovo elemento dal menu Progetto.

  7. Selezionare il modello Classe, modificare il nome in SampleIPrincipal, quindi fare clic su Aggiungi.

  8. Scegliere Proprietà <ApplicationName> dal menu Progetto.

  9. Scegliere la scheda Applicazione in Progettazione progetti.

  10. Modificare l'elenco a discesa Modalità autenticazione in Definita dall'applicazione.

Per configurare il form principale

  1. Passare a Form1 in Progettazione Form.

  2. Dalla Casella degli strumenti aggiungere un Pulsante a Form1.

    Il nome predefinito del pulsante è Button1.

  3. Modificare il testo del pulsante in Autentica.

  4. Dalla Casella degli strumenti, aggiungere un'Etichetta al Form1.

    Il nome predefinito dell'etichetta è Label1.

  5. Modificare il testo dell'etichetta in una stringa vuota.

  6. Dalla Casella degli strumenti, aggiungere un'Etichetta al Form1.

    Il nome predefinito dell'etichetta è Label2.

  7. Modificare il testo dell'etichetta in una stringa vuota.

  8. Fare doppio clic su Button1 per creare un gestore eventi per l'evento Click, quindi aprire l'editor di codice.

  9. Aggiungere al metodo Button1_Click il seguente codice.

    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
    

È possibile eseguire l'applicazione. Tuttavia, poiché non è presente alcun codice di autenticazione, gli utenti non saranno autenticati. Nella seguente sezione viene descritta la procedura per l'aggiunta del codice di autenticazione.

Creazione di un'identità

In .NET Framework vengono utilizzate le interfacce IIdentity e IPrincipal come base per l'autenticazione e l'autorizzazione. Mediante l'implementazione di queste interfacce, è possibile utilizzare l'autenticazione utente personalizzata, come illustrato dalle procedure riportate di seguito.

Per creare una classe che implementa IIdentity

  1. Selezionare il file SampleIIdentity.vb in Esplora soluzioni.

    Nella classe è incapsulata l'identità di un utente.

  2. Nella riga che segue Public Class SampleIIdentity, aggiungere il seguente codice per ereditare dalla IIdentity.

    Implements System.Security.Principal.IIdentity
    

    Dopo aver aggiunto il codice e aver premuto INVIO, mediante l'editor di codice vengono create le proprietà stub da implementare.

  3. Aggiungere campi privati per archiviare il nome utente e un valore che indica se l'utente è autenticato.

    Private nameValue As String
    Private authenticatedValue As Boolean
    Private roleValue As ApplicationServices.BuiltInRole
    
  4. Immettere il seguente codice nella proprietà AuthenticationType.

    La proprietà AuthenticationType dovrà restituire una stringa che consente di specificare il meccanismo di autenticazione corrente.

    Nell'esempio riportato di seguito viene utilizzata un'autenticazione specificata in modo esplicito, in modo che la stringa sia "Autenticazione personalizzata". Se i dati di autenticazione dell'utente sono stati memorizzati in un database SQL Server, è probabile che il valore sia "SqlDatabase".

    Return "Custom Authentication"
    
  5. Immettere il seguente codice nella proprietà IsAuthenticated.

    Return authenticatedValue
    

    La proprietà IsAuthenticated dovrà restituire un valore che indica se l'utente è stato autenticato.

  6. La proprietà Name dovrà restituire un il nome dell'utente associato all'identità.

    Immettere il seguente codice nella proprietà Name.

    Return nameValue
    
  7. Creare una proprietà che restituisce il ruolo dell'utente.

    Public ReadOnly Property Role() As ApplicationServices.BuiltInRole
        Get
            Return roleValue
        End Get
    End Property
    
  8. Creare un metodo Sub New che consente di inizializzare la classe autenticando l'utente e impostando il nome e ruolo dell'utente in base a un nome e a una password.

    Questo metodo esegue la chiamata al metodo denominato IsValidNameAndPassword per determinare la validità di una combinazione di nome utente e password.

    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. Creare un metodo denominato IsValidNameAndPassword che consente di determinare la validità di una combinazione di nome utente e password.

    Nota sulla sicurezzaNota sulla sicurezza

    L'algoritmo di autenticazione deve gestire le password in modo protetto. Ad esempio, la password non deve essere archiviata in un campo di classe.

    Non memorizzare le password utente nel sistema, poiché in caso di perdita delle informazioni, il sistema non è più protetto. Memorizzare l'hash della password di ciascun utente (una funzione hash codifica i dati in modo che non sia possibile dedurre l'input dall'output). Non è possibile determinare una password direttamente dal relativo hash.

    Tuttavia, un utente malintenzionato potrebbe generare un dizionario degli hash di tutte le possibili password, quindi ricercare la password di un determinato hash. Per proteggersi da questo tipo di attacco, prima di generare un hash per la password, aggiungere un valore salt in modo da generare un hash con salt. Il valore salt costituisce dati aggiuntivi che sono univoci per ciascuna password, rendendo impossibile la compilazione di un dizionario di hash.

    Per proteggere le password dagli utenti malintenzionati, memorizzare solo gli hash con salt delle password, preferibilmente su un computer protetto. È molto difficile recuperare una password da un hash con salt. Nell'esempio riportato di seguito vengono utilizzati i metodi GetHashedPassword e GetSalt per caricare la password con hash e il salt di un utente.

    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. Creare funzioni denominate GetHashedPassword e GetSalt che restituiscono le password con hash e il salt dell'utente specificato.

    Nota sulla sicurezzaNota sulla sicurezza

    Si sconsiglia di definire a livello di codice le password con hash e i salt nelle applicazioni client per due motivi. Innanzitutto, gli utenti malintenzionati potrebbero riuscire ad avere accesso e individuare una collisione hash. In secondo luogo, non è possibile modificare o revocare la password di un utente. La password con hash e il salt di un determinato utente dovrebbero essere ottenute da un'origine protetta mantenuta da un'amministratore.

    Anche se, per semplicità, nell'esempio riportato di seguito è descritta una password definita a livello di codice, con hash e salt, nel codice di produzione si consiglia di utilizzare un metodo più protetto. Ad esempio, è possibile memorizzare le informazioni utente in un database SQL Server e accedervi con le stored procedure. Per ulteriori informazioni, vedere Procedura: connettersi ai dati di un database.

    Nota

    La password corrispondente alla password con hash definita a livello di codice viene fornita nella sezione "Test dell'applicazione".

    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
    

Nel file SampleIIdentity.vb dovrebbe ora essere contenuto il seguente codice:

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

Creazione di un principale

Quindi, è necessario implementare una classe che deriva dalla IPrincipal e che restituisce istanze della classe SampleIIdentity.

Per creare una classe che implementa IPrincipal

  1. Selezionare il file SampleIPrincipal.vb in Esplora soluzioni.

    Nella classe è incapsulata l'identità di un utente. È possibile utilizzare l'oggetto My.User per associare questo principale al thread corrente e accedere all'identità dell'utente.

  2. Nella riga che segue Public Class SampleIPrincipal, aggiungere il seguente codice per ereditare dalla IPrincipal.

    Implements System.Security.Principal.IPrincipal
    

    Dopo aver aggiunto il codice e aver premuto INVIO, mediante l'editor di codice viene creata una proprietà stub e il metodo da implementare.

  3. Aggiungere un campo privato per memorizzare l'identità associata al principale.

    Private identityValue As SampleIIdentity
    
  4. Immettere il seguente codice nella proprietà Identity.

    Return identityValue
    

    La proprietà Identity deve restituire l'identità dell'utente del principale corrente.

  5. Immettere il seguente codice nel metodo IsInRole.

    Il metodo IsInRole consente di determinare se il principale corrente appartiene al ruolo specificato.

    Return role = identityValue.Role.ToString
    
  6. Creare un metodo Sub New che consente di inizializzare la classe con una nuova istanza di SampleIIdentity in base a un nome utente e password.

    Public Sub New(ByVal name As String, ByVal password As String)
        identityValue = New SampleIIdentity(name, password)
    End Sub
    

    Mediante il codice è possibile impostare l'identità dell'utente per la classe SampleIPrincipal.

Nel file SampleIPrincipal.vb dovrebbe ora essere contenuto il seguente codice:

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

Connessione del form di accesso

È possibile utilizzare il form di accesso per ottenere un nome utente e la password. Queste informazioni possono essere utilizzate per inizializzare un'istanza della classe SampleIPrincipal e utilizzare l'oggetto My.User per impostare l'identità del thread corrente dell'istanza.

Per configurare il form di accesso

  1. Selezionare LoginForm1 nella progettazione.

  2. Fare doppio clic sul pulsante OK per aprire l'editor di codice per l'evento Click.

  3. Sostituire il codice nel metodo OK_Click con il codice seguente.

    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
    

Verifica dell'applicazione

Ora che all'applicazione è associato un codice di autenticazione, è possibile eseguire l'applicazione e tentare di autenticare un utente.

Per eseguire il test dell'applicazione

  1. Avviare l'applicazione.

  2. Fare clic su Autentica.

    Verrà aperto il form di accesso.

  3. Immettere TestUser nella casella Nome utente e BadPassword nella casella Password, quindi fare clic su OK.

    Verrà visualizzata una finestra di messaggio per segnalare che il nome utente e la password non sono validi.

  4. Scegliere OK per chiudere la finestra del messaggio.

  5. Fare clic su Annulla per chiudere il form di accesso.

    Nelle etichette del form principale viene ora dichiarato Utente non autenticato e L'utente non è un Administrator.

  6. Fare clic su Autentica.

    Verrà aperto il form di accesso.

  7. Immettere TestUser nella casella di testo Nome utente e Password nella casella di testo Password, quindi fare clic su OK. Accertarsi che la password sia inserita con le lettere maiuscole e minuscole corrette.

    Nelle etichette del form principale viene ora dichiarato TestUser autenticato e L'utente è un Administrator.

Vedere anche

Attività

Procedura: connettersi ai dati di un database

Riferimenti

User

IIdentity

IPrincipal

Concetti

Accesso ai dati utente (Visual Basic)

Altre risorse

Autenticazione e autorizzazione in .NET Framework con Visual Basic