Procédure pas à pas : implémentation d'une authentification et d'une autorisation personnalisées
Mise à jour : novembre 2007
Cette procédure pas à pas montre comment implémenter une authentification et une autorisation personnalisées à l'aide de classes qui dérivent de IIdentity et de IPrincipal. Elle montre également comment substituer l'identité par défaut du thread de l'application, l'identité Windows, en affectant à My.User.CurrentPrincipal une instance de la classe qui dérive de IPrincipal. Les nouvelles informations utilisateur sont immédiatement disponibles via l'objet My.User qui retourne des informations sur l'identité de l'utilisateur en cours du thread.
Les applications destinées aux entreprises accordent souvent l'accès aux données et aux ressources en fonction des informations d'identification fournies par l'utilisateur. Pour cela, elles vérifient en général le rôle de l'utilisateur afin de lui donner accès aux ressources en rapport avec son rôle. Le Common Language Runtime fournit une prise en charge des autorisations basées sur les rôles pour des comptes Windows ou des identités personnalisées. Pour plus d'informations, consultez Sécurité basée sur les rôles.
Mise en route
Pour commencer, installez un projet avec un formulaire principal et un formulaire de connexion et configurez-le pour utiliser une authentification personnalisée.
Pour créer l'exemple d'application
Créez un projet d'application Windows Visual Basic. Pour plus d'informations, consultez Comment : créer un projet d'application Windows.
Le nom par défaut du formulaire principal est Form1.
Dans le menu Projet, cliquez sur Ajouter un nouvel élément.
Sélectionnez le modèle Formulaire de connexion et cliquez sur Ajouter.
Le nom par défaut du formulaire de connexion est LoginForm1.
Dans le menu Projet, cliquez sur Ajouter un nouvel élément.
Sélectionnez le modèle Classe, remplacez le nom par SampleIIdentity, puis cliquez sur Ajouter.
Dans le menu Projet, cliquez sur Ajouter un nouvel élément.
Sélectionnez le modèle Classe, remplacez le nom par SampleIPrincipal, puis cliquez sur Ajouter.
Dans le menu Projet, cliquez sur Propriétés <NomApplication>.
Dans le Concepteur de projets, cliquez sur l'onglet Application.
Remplacez la liste déroulante Mode d'authentification par Défini au niveau de l'application.
Pour configurer le formulaire principal
Basculez vers Form1 dans le Concepteur de formulaires.
Ajoutez un Bouton à Form1 dans la Boîte à outils.
Le nom par défaut du bouton est Button1.
Remplacez le texte du bouton par Authentifier.
Dans la Boîte à outils, ajoutez une Étiquette à Form1.
Le nom par défaut de l'étiquette est Label1.
Remplacez le texte de l'étiquette par une chaîne vide.
Dans la Boîte à outils, ajoutez une Étiquette à Form1.
Le nom par défaut de l'étiquette est Label2.
Remplacez le texte de l'étiquette par une chaîne vide.
Double-cliquez sur Button1 pour créer le gestionnaire d'événements pour l'événement Click, puis ouvrez l'éditeur de code.
Ajoutez le code suivant à la méthode 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
Vous pouvez lancer l'application, mais étant donné qu'elle ne contient pas de code d'authentification, elle n'authentifiera pas d'utilisateur. L'ajout d'un code d'authentification est abordé dans la section suivante.
Création d'une identité
Le .NET Framework utilise les interfaces IIdentity et IPrincipal en tant que base de l'authentification et de l'autorisation. Votre application peut utiliser une authentification utilisateur personnalisée en implémentant ces interfaces, comme le montrent ces procédures.
Pour créer une classe qui implémente IIdentity
Sélectionnez le fichier SampleIIdentity.vb dans l'Explorateur de solutions.
Cette classe encapsule l'identité d'un utilisateur.
Dans la ligne Public Class SampleIIdentity suivante, ajoutez le code suivant pour hériter de IIdentity.
Implements System.Security.Principal.IIdentity
Une fois que vous avez ajouté le code et appuyé sur ENTRÉE, l'éditeur de code crée les propriétés stub que vous devez implémenter.
Ajoutez des champs privés pour stocker le nom d'utilisateur et une valeur qui indique si l'utilisateur est authentifié.
Private nameValue As String Private authenticatedValue As Boolean Private roleValue As ApplicationServices.BuiltInRole
Entrez le code suivant dans la propriété AuthenticationType.
La propriété AuthenticationType doit retourner une chaîne qui indique le mécanisme d'authentification actuel.
Cet exemple utilise l'authentification spécifiée explicitement, donc la chaîne est "Authentification personnalisée". Si les données d'authentification de l'utilisateur ont été stockées dans une base de données SQL Server, la valeur peut être "SqlDatabase".
Return "Custom Authentication"
Entrez le code suivant dans la propriété IsAuthenticated.
Return authenticatedValue
La propriété IsAuthenticated doit retourner une valeur qui indique si l'utilisateur a été authentifié.
La propriété Name doit retourner le nom de l'utilisateur associé à cette identité.
Entrez le code suivant dans la propriété Name.
Return nameValue
Créez une propriété qui retourne le rôle de l'utilisateur.
Public ReadOnly Property Role() As ApplicationServices.BuiltInRole Get Return roleValue End Get End Property
Créez une méthode Sub New qui initialise la classe en authentifiant l'utilisateur, puis en définissant son nom et son rôle, sur la base d'un nom et d'un mot de passe.
Cette méthode appelle une méthode nommée IsValidNameAndPassword pour déterminer si une association nom d'utilisateur/mot de passe est valide.
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
Créez une méthode nommée IsValidNameAndPassword qui détermine si une association nom d'utilisateur/mot de passe est valide.
Note de sécurité : L'algorithme d'authentification doit traiter les mots de passe de manière sécurisée. Par exemple, le mot de passe ne doit pas être stocké dans un champ de classe.
Vous ne devez pas stocker les mots de passe utilisateur dans votre système, parce que si ces informations sont perdues, il n'y a plus de sécurité. Vous pouvez stocker le hachage du mot de passe de chaque utilisateur. Une fonction de hachage brouille les données de sorte que l'entrée ne puisse pas être déduite de la sortie. Un mot de passe ne peut pas être déterminé directement à partir du hachage d'un autre.
Toutefois, un utilisateur malveillant pourrait prendre le temps de générer un dictionnaire des hachages de tous les mots de passe possibles, puis rechercher un hachage donné d'un mot de passe. Pour se prémunir contre ce type d'attaque, vous devez ajouter un salt au mot de passe avant de le hacher pour générer un hachage salted. Le salt est constitué de données supplémentaires uniques à chaque mot de passe, empêchant ainsi le précalcul d'un dictionnaire de hachage.
Pour protéger les mots de passe contre les utilisateurs malveillants, vous ne devez stocker que des hachages salted des mots de passe, de préférence sur un ordinateur sécurisé. Il est très difficile pour un utilisateur malveillant de récupérer un mot de passe d'un hachage salted. Cet exemple utilise les méthodes GetHashedPassword et GetSalt pour charger le mot de passe haché et le salt d'un utilisateur.
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
Créez les fonctions appelées GetHashedPassword et GetSalt qui retournent le mot de passe haché et le salt pour l'utilisateur spécifié.
Note de sécurité : Vous devez éviter le codage en dur des mots de passe hachés et des salts dans vos applications clientes pour deux raisons. D'abord, des utilisateurs malveillants peuvent y accéder et trouver une collision de hachage. Ensuite, vous ne pouvez pas modifier ni révoquer le mot de passe d'un utilisateur. L'application doit obtenir le mot de passe haché et le salt pour un utilisateur donné à partir d'une source sécurisée gérée par un administrateur.
Même si, pour des raisons de simplicité, cet exemple a un mot de passe haché et un salt codés en dur, vous devez utiliser une approche plus sécurisée dans le code de production. Par exemple, vous pouvez stocker les informations utilisateur dans une base de données SQL Server et y accéder à l'aide de procédures stockées. Pour plus d'informations, consultez Comment : établir une connexion à des données d'une base de données.
Remarque : Le mot de passe correspondant à ce mot de passe haché codé en dur est fourni dans la section « Test de l'application ».
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
Le fichier SampleIIdentity.vb doit désormais contenir le code suivant :
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
Création d'une entité de sécurité
Ensuite, vous devez implémenter une classe qui dérive de IPrincipal et la configurer pour qu'elle retourne des instances de la classe SampleIIdentity.
Pour créer une classe qui implémente IPrincipal
Sélectionnez le fichier SampleIPrincipal.vb dans l'Explorateur de solutions.
Cette classe encapsule l'identité d'un utilisateur. Vous pouvez utiliser l'objet My.User pour joindre cette entité de sécurité au thread actuel et accéder à l'identité de l'utilisateur.
Dans la ligne Public Class SampleIPrincipal suivante, ajoutez le code suivant pour hériter de IPrincipal.
Implements System.Security.Principal.IPrincipal
Une fois que vous avez ajouté le code et appuyé sur ENTRÉE, créez une propriété et une méthode stub que vous devez implémenter.
Ajoutez un champ privé pour stocker l'identité associée à cette entité de sécurité.
Private identityValue As SampleIIdentity
Entrez le code suivant dans la propriété Identity.
Return identityValue
La propriété Identity doit retourner l'identité de l'utilisateur de l'entité de sécurité actuelle.
Entrez le code suivant dans la méthode IsInRole.
La méthode IsInRole détermine si l'entité de sécurité actuelle appartient au rôle spécifié.
Return role = identityValue.Role.ToString
Créez une méthode Sub New qui initialise la classe à l'aide d'une nouvelle instance de SampleIIdentity avec un nom d'utilisateur et un mot de passe.
Public Sub New(ByVal name As String, ByVal password As String) identityValue = New SampleIIdentity(name, password) End Sub
Ce code définit l'identité de l'utilisateur pour la classe SampleIPrincipal.
Le fichier SampleIPrincipal.vb doit désormais contenir le code suivant :
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
Connexion du formulaire de connexion
L'application peut utiliser le formulaire de connexion pour collecter un nom d'utilisateur et un mot de passe. Elle peut utiliser ces informations pour initialiser une instance de la classe SampleIPrincipal et utiliser l'objet My.User pour affecter cette instance à l'identité du thread actuel.
Pour configurer le formulaire de connexion
Sélectionnez LoginForm1 dans le concepteur.
Double-cliquez sur le bouton OK pour ouvrir l'éditeur de code pour l'événement Click.
Remplacez le code dans la méthode OK_Click par le code suivant :
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
Test de l'application
Maintenant que l'application a un code d'authentification, vous pouvez l'exécuter et essayer d'authentifier un utilisateur.
Pour tester l'application
Démarrez l'application.
Cliquez sur Authentifier.
Le formulaire de connexion s'ouvre.
Entrez TestUser dans la zone Nom d'utilisateur et BadPassword dans la zone Mot de passe, puis cliquez sur OK.
Un message s'affiche et indique que la paire nom d'utilisateur/mot de passe est incorrecte.
Cliquez sur OK pour fermer le message.
Cliquer sur Annuler pour fermer le formulaire de connexion.
Les étiquettes du formulaire principal indiquent désormais User not authenticated et User is not an Administrator.
Cliquez sur Authentifier.
Le formulaire de connexion s'ouvre.
Entrez TestUser dans la zone de texte Nom d'utilisateur et Password dans la zone de texte Mot de passe, puis cliquez sur OK. Vérifiez que le mot de passe est entré avec le respect des majuscules.
Les étiquettes du formulaire principal affichent désormais Authenticated TestUser et User is an Administrator.
Voir aussi
Tâches
Comment : établir une connexion à des données d'une base de données
Concepts
Accès aux données de l'utilisateur
Référence
Autres ressources
Authentification et autorisation dans le .NET Framework avec Visual Basic