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
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.
Scegliere Aggiungi nuovo elemento dal menu Progetto.
Selezionare il modello Form di accesso e fare clic su Aggiungi.
Il nome predefinito del form di accesso è LoginForm1.
Scegliere Aggiungi nuovo elemento dal menu Progetto.
Selezionare il modello Classe, modificare il nome in SampleIIdentity, quindi fare clic su Aggiungi.
Scegliere Aggiungi nuovo elemento dal menu Progetto.
Selezionare il modello Classe, modificare il nome in SampleIPrincipal, quindi fare clic su Aggiungi.
Scegliere Proprietà <ApplicationName> dal menu Progetto.
Scegliere la scheda Applicazione in Progettazione progetti.
Modificare l'elenco a discesa Modalità autenticazione in Definita dall'applicazione.
Per configurare il form principale
Passare a Form1 in Progettazione Form.
Dalla Casella degli strumenti aggiungere un Pulsante a Form1.
Il nome predefinito del pulsante è Button1.
Modificare il testo del pulsante in Autentica.
Dalla Casella degli strumenti, aggiungere un'Etichetta al Form1.
Il nome predefinito dell'etichetta è Label1.
Modificare il testo dell'etichetta in una stringa vuota.
Dalla Casella degli strumenti, aggiungere un'Etichetta al Form1.
Il nome predefinito dell'etichetta è Label2.
Modificare il testo dell'etichetta in una stringa vuota.
Fare doppio clic su Button1 per creare un gestore eventi per l'evento Click, quindi aprire l'editor di codice.
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
Selezionare il file SampleIIdentity.vb in Esplora soluzioni.
Nella classe è incapsulata l'identità di un utente.
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.
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
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"
Immettere il seguente codice nella proprietà IsAuthenticated.
Return authenticatedValue
La proprietà IsAuthenticated dovrà restituire un valore che indica se l'utente è stato autenticato.
La proprietà Name dovrà restituire un il nome dell'utente associato all'identità.
Immettere il seguente codice nella proprietà Name.
Return nameValue
Creare una proprietà che restituisce il ruolo dell'utente.
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole Get Return roleValue End Get End Property
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
Creare un metodo denominato IsValidNameAndPassword che consente di determinare la validità di una combinazione di nome utente e password.
Nota 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
Creare funzioni denominate GetHashedPassword e GetSalt che restituiscono le password con hash e il salt dell'utente specificato.
Nota 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
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.
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.
Aggiungere un campo privato per memorizzare l'identità associata al principale.
Private identityValue As SampleIIdentity
Immettere il seguente codice nella proprietà Identity.
Return identityValue
La proprietà Identity deve restituire l'identità dell'utente del principale corrente.
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
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
Selezionare LoginForm1 nella progettazione.
Fare doppio clic sul pulsante OK per aprire l'editor di codice per l'evento Click.
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
Avviare l'applicazione.
Fare clic su Autentica.
Verrà aperto il form di accesso.
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.
Scegliere OK per chiudere la finestra del messaggio.
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.
Fare clic su Autentica.
Verrà aperto il form di accesso.
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
Concetti
Accesso ai dati utente (Visual Basic)
Altre risorse
Autenticazione e autorizzazione in .NET Framework con Visual Basic