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

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

  2. Dans le menu Projet, cliquez sur Ajouter un nouvel élément.

  3. Sélectionnez le modèle Formulaire de connexion et cliquez sur Ajouter.

    Le nom par défaut du formulaire de connexion est LoginForm1.

  4. Dans le menu Projet, cliquez sur Ajouter un nouvel élément.

  5. Sélectionnez le modèle Classe, remplacez le nom par SampleIIdentity, puis cliquez sur Ajouter.

  6. Dans le menu Projet, cliquez sur Ajouter un nouvel élément.

  7. Sélectionnez le modèle Classe, remplacez le nom par SampleIPrincipal, puis cliquez sur Ajouter.

  8. Dans le menu Projet, cliquez sur Propriétés <NomApplication>.

  9. Dans le Concepteur de projets, cliquez sur l'onglet Application.

  10. Remplacez la liste déroulante Mode d'authentification par Défini au niveau de l'application.

Pour configurer le formulaire principal

  1. Basculez vers Form1 dans le Concepteur de formulaires.

  2. Ajoutez un Bouton à Form1 dans la Boîte à outils.

    Le nom par défaut du bouton est Button1.

  3. Remplacez le texte du bouton par Authentifier.

  4. Dans la Boîte à outils, ajoutez une Étiquette à Form1.

    Le nom par défaut de l'étiquette est Label1.

  5. Remplacez le texte de l'étiquette par une chaîne vide.

  6. Dans la Boîte à outils, ajoutez une Étiquette à Form1.

    Le nom par défaut de l'étiquette est Label2.

  7. Remplacez le texte de l'étiquette par une chaîne vide.

  8. Double-cliquez sur Button1 pour créer le gestionnaire d'événements pour l'événement Click, puis ouvrez l'éditeur de code.

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

  1. Sélectionnez le fichier SampleIIdentity.vb dans l'Explorateur de solutions.

    Cette classe encapsule l'identité d'un utilisateur.

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

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

  6. 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
    
  7. 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
    
  8. 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
    
  9. 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
    
  10. 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

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

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

  3. Ajoutez un champ privé pour stocker l'identité associée à cette entité de sécurité.

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

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

  1. Sélectionnez LoginForm1 dans le concepteur.

  2. Double-cliquez sur le bouton OK pour ouvrir l'éditeur de code pour l'événement Click.

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

  1. Démarrez l'application.

  2. Cliquez sur Authentifier.

    Le formulaire de connexion s'ouvre.

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

  4. Cliquez sur OK pour fermer le message.

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

  6. Cliquez sur Authentifier.

    Le formulaire de connexion s'ouvre.

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

My.User, objet

IIdentity

IPrincipal

Autres ressources

Authentification et autorisation dans le .NET Framework avec Visual Basic