Partager via


Stratégie d’autorisation

Cet exemple montre comment implémenter une stratégie d’autorisation de revendication personnalisée et un gestionnaire d’autorisations de service personnalisé associé. Cela est utile lorsque le service effectue des vérifications d’accès basées sur les revendications pour les opérations de service, et avant ces vérifications, accorde certains droits à l’appelant. Cet exemple montre à la fois le processus d’ajout de revendications ainsi que le processus d’exécution d’une vérification d’accès par rapport à l’ensemble finalisé de revendications. Tous les messages d’application entre le client et le serveur sont signés et chiffrés. Par défaut, avec la wsHttpBinding liaison, un nom d’utilisateur et un mot de passe fournis par le client sont utilisés pour se connecter à un compte Windows valide. Cet exemple montre comment utiliser un personnalisé UserNamePasswordValidator pour authentifier le client. En outre, cet exemple montre le client s’authentifiant auprès du service à l’aide d’un certificat X.509. Cet exemple montre une implémentation de IAuthorizationPolicy et ServiceAuthorizationManager, qui ensemble accordent l'accès à des méthodes spécifiques du service pour des utilisateurs particuliers. Cet exemple est basé sur le nom d’utilisateur de sécurité des messages, mais montre comment effectuer une transformation de demandes avant que ServiceAuthorizationManager ne soit appelé.

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cette rubrique.

En résumé, cet exemple montre comment :

  • Le client peut être authentifié à l’aide d’un nom d’utilisateur-mot de passe.

  • Le client peut être authentifié à l’aide d’un certificat X.509.

  • Le serveur valide les informations d’identification du client par rapport à un validateur personnalisé UsernamePassword .

  • Le serveur est authentifié à l’aide du certificat X.509 du serveur.

  • Le serveur peut utiliser ServiceAuthorizationManager pour contrôler l’accès à certaines méthodes du service.

  • Comment implémenter IAuthorizationPolicy.

Le service expose deux points de terminaison pour communiquer avec le service, définis à l’aide du fichier de configuration App.config. Chaque point de terminaison se compose d’une adresse, d’une liaison et d’un contrat. Une liaison est configurée avec une liaison standard wsHttpBinding qui utilise WS-Security et l’authentification du nom d’utilisateur du client. L’autre liaison est configurée avec une liaison standard wsHttpBinding qui utilise WS-Security et l’authentification par certificat client. Le <comportement> spécifie que les informations d’identification de l’utilisateur doivent être utilisées pour l’authentification du service. Le certificat de serveur doit contenir la même valeur pour la SubjectName propriété que l’attribut findValue dans le< serviceCertificate>.

<system.serviceModel>
  <services>
    <service name="Microsoft.ServiceModel.Samples.CalculatorService"
             behaviorConfiguration="CalculatorServiceBehavior">
      <host>
        <baseAddresses>
          <!-- configure base address provided by host -->
          <add baseAddress ="http://localhost:8001/servicemodelsamples/service"/>
        </baseAddresses>
      </host>
      <!-- use base address provided by host, provide two endpoints -->
      <endpoint address="username"
                binding="wsHttpBinding"
                bindingConfiguration="Binding1"
                contract="Microsoft.ServiceModel.Samples.ICalculator" />
      <endpoint address="certificate"
                binding="wsHttpBinding"
                bindingConfiguration="Binding2"
                contract="Microsoft.ServiceModel.Samples.ICalculator" />
    </service>
  </services>

  <bindings>
    <wsHttpBinding>
      <!-- Username binding -->
      <binding name="Binding1">
        <security mode="Message">
    <message clientCredentialType="UserName" />
        </security>
      </binding>
      <!-- X509 certificate binding -->
      <binding name="Binding2">
        <security mode="Message">
          <message clientCredentialType="Certificate" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>

  <behaviors>
    <serviceBehaviors>
      <behavior name="CalculatorServiceBehavior" >
        <serviceDebug includeExceptionDetailInFaults ="true" />
        <serviceCredentials>
          <!--
          The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
          -->
          <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
          <!--
          The serviceCredentials behavior allows one to specify authentication constraints on client certificates.
          -->
          <clientCertificate>
            <!--
            Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate
            is in the user's Trusted People store, then it will be trusted without performing a
            validation of the certificate's issuer chain. This setting is used here for convenience so that the
            sample can be run without having to have certificates issued by a certification authority (CA).
            This setting is less secure than the default, ChainTrust. The security implications of this
            setting should be carefully considered before using PeerOrChainTrust in production code.
            -->
            <authentication certificateValidationMode="PeerOrChainTrust" />
          </clientCertificate>
          <!--
          The serviceCredentials behavior allows one to define a service certificate.
          A service certificate is used by a client to authenticate the service and provide message protection.
          This configuration references the "localhost" certificate installed during the setup instructions.
          -->
          <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
        </serviceCredentials>
        <serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
          <!--
          The serviceAuthorization behavior allows one to specify custom authorization policies.
          -->
          <authorizationPolicies>
            <add policyType="Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary" />
          </authorizationPolicies>
        </serviceAuthorization>
      </behavior>
    </serviceBehaviors>
  </behaviors>

</system.serviceModel>

Chaque configuration de point de terminaison client se compose d’un nom de configuration, d’une adresse absolue pour le point de terminaison de service, de la liaison et du contrat. La liaison cliente est configurée avec le mode de sécurité approprié tel que spécifié dans ce cas dans la <sécurité> et clientCredentialType comme spécifié dans le <message>.

<system.serviceModel>

    <client>
      <!-- Username based endpoint -->
      <endpoint name="Username"
            address="http://localhost:8001/servicemodelsamples/service/username"
    binding="wsHttpBinding"
    bindingConfiguration="Binding1"
                behaviorConfiguration="ClientCertificateBehavior"
                contract="Microsoft.ServiceModel.Samples.ICalculator" >
      </endpoint>
      <!-- X509 certificate based endpoint -->
      <endpoint name="Certificate"
                        address="http://localhost:8001/servicemodelsamples/service/certificate"
                binding="wsHttpBinding"
            bindingConfiguration="Binding2"
                behaviorConfiguration="ClientCertificateBehavior"
                contract="Microsoft.ServiceModel.Samples.ICalculator">
      </endpoint>
    </client>

    <bindings>
      <wsHttpBinding>
        <!-- Username binding -->
      <binding name="Binding1">
        <security mode="Message">
          <message clientCredentialType="UserName" />
        </security>
      </binding>
        <!-- X509 certificate binding -->
        <binding name="Binding2">
          <security mode="Message">
            <message clientCredentialType="Certificate" />
          </security>
        </binding>
    </wsHttpBinding>
    </bindings>

    <behaviors>
      <behavior name="ClientCertificateBehavior">
        <clientCredentials>
          <serviceCertificate>
            <!--
            Setting the certificateValidationMode to PeerOrChainTrust
            means that if the certificate
            is in the user's Trusted People store, then it will be
            trusted without performing a
            validation of the certificate's issuer chain. This setting
            is used here for convenience so that the
            sample can be run without having to have certificates
            issued by a certification authority (CA).
            This setting is less secure than the default, ChainTrust.
            The security implications of this
            setting should be carefully considered before using
            PeerOrChainTrust in production code.
            -->
            <authentication certificateValidationMode = "PeerOrChainTrust" />
          </serviceCertificate>
        </clientCredentials>
      </behavior>
    </behaviors>

  </system.serviceModel>

Pour le point de terminaison basé sur le nom d’utilisateur, l’implémentation du client définit le nom d’utilisateur et le mot de passe à utiliser.

// Create a client with Username endpoint configuration
CalculatorClient client1 = new CalculatorClient("Username");

client1.ClientCredentials.UserName.UserName = "test1";
client1.ClientCredentials.UserName.Password = "1tset";

try
{
    // Call the Add service operation.
    double value1 = 100.00D;
    double value2 = 15.99D;
    double result = client1.Add(value1, value2);
    Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
    ...
}
catch (Exception e)
{
    Console.WriteLine("Call failed : {0}", e.Message);
}

client1.Close();

Pour le point de terminaison basé sur un certificat, l’implémentation du client définit le certificat client à utiliser.

// Create a client with Certificate endpoint configuration
CalculatorClient client2 = new CalculatorClient("Certificate");

client2.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "test1");

try
{
    // Call the Add service operation.
    double value1 = 100.00D;
    double value2 = 15.99D;
    double result = client2.Add(value1, value2);
    Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
    ...
}
catch (Exception e)
{
    Console.WriteLine("Call failed : {0}", e.Message);
}

client2.Close();

Cet exemple utilise un code personnalisé UserNamePasswordValidator pour valider les noms d’utilisateur et les mots de passe. L’exemple implémente MyCustomUserNamePasswordValidator, dérivé de UserNamePasswordValidator. Consultez la documentation concernant UserNamePasswordValidator pour plus d'informations. À des fins de démonstration de l’intégration avec le UserNamePasswordValidator, cet exemple de validateur personnalisé implémente la Validate méthode pour accepter les paires nom d’utilisateur/mot de passe où le nom d’utilisateur correspond au mot de passe, comme indiqué dans le code suivant.

public class MyCustomUserNamePasswordValidator : UserNamePasswordValidator
{
  // This method validates users. It allows in two users,
  // test1 and test2 with passwords 1tset and 2tset respectively.
  // This code is for illustration purposes only and
  // MUST NOT be used in a production environment because it
  // is NOT secure.
  public override void Validate(string userName, string password)
  {
    if (null == userName || null == password)
    {
      throw new ArgumentNullException();
    }

    if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
    {
      throw new SecurityTokenException("Unknown Username or Password");
    }
  }
}

Une fois le validateur implémenté dans le code de service, l’hôte de service doit être informé de l’instance de validateur à utiliser. Pour ce faire, utilisez le code suivant :

Servicehost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomUserNamePasswordValidatorProvider();

Vous pouvez également faire la même chose dans la configuration :

<behavior>
    <serviceCredentials>
      <!--
      The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
      -->
      <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
    ...
    </serviceCredentials>
</behavior>

Windows Communication Foundation (WCF) fournit un modèle riche basé sur les revendications pour effectuer des vérifications d’accès. L’objet ServiceAuthorizationManager est utilisé pour effectuer la vérification d’accès et déterminer si les revendications associées au client répondent aux exigences nécessaires pour accéder à la méthode de service.

Dans le cadre de la démonstration, cet exemple montre une implémentation de ServiceAuthorizationManager qui réalise la méthode CheckAccessCore pour permettre à un utilisateur d’accéder aux méthodes en fonction de revendications de type http://example.com/claims/allowedoperation, dont la valeur correspond à l’URI d’action de l’opération autorisée à être appelée.

public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
  protected override bool CheckAccessCore(OperationContext operationContext)
  {
    string action = operationContext.RequestContext.RequestMessage.Headers.Action;
    Console.WriteLine("action: {0}", action);
    foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
    {
      if ( cs.Issuer == ClaimSet.System )
      {
        foreach (Claim c in cs.FindClaims("http://example.com/claims/allowedoperation", Rights.PossessProperty))
        {
          Console.WriteLine("resource: {0}", c.Resource.ToString());
          if (action == c.Resource.ToString())
            return true;
        }
      }
    }
    return false;
  }
}

Une fois le composant personnalisé ServiceAuthorizationManager implémenté, l’hôte de service doit être informé à propos de ServiceAuthorizationManager que l'on doit utiliser. Cette opération est effectuée comme indiqué dans le code suivant.

<behavior>
    ...
    <serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
        ...
    </serviceAuthorization>
</behavior>

La principale méthode IAuthorizationPolicy à implémenter est la méthode Evaluate(EvaluationContext, Object).

public class MyAuthorizationPolicy : IAuthorizationPolicy
{
    string id;

    public MyAuthorizationPolicy()
    {
    id =  Guid.NewGuid().ToString();
    }

    public bool Evaluate(EvaluationContext evaluationContext,
                                            ref object state)
    {
        bool bRet = false;
        CustomAuthState customstate = null;

        if (state == null)
        {
            customstate = new CustomAuthState();
            state = customstate;
        }
        else
            customstate = (CustomAuthState)state;
        Console.WriteLine("In Evaluate");
        if (!customstate.ClaimsAdded)
        {
           IList<Claim> claims = new List<Claim>();

           foreach (ClaimSet cs in evaluationContext.ClaimSets)
              foreach (Claim c in cs.FindClaims(ClaimTypes.Name,
                                         Rights.PossessProperty))
                  foreach (string s in
                        GetAllowedOpList(c.Resource.ToString()))
                  {
                       claims.Add(new
               Claim("http://example.com/claims/allowedoperation",
                                    s, Rights.PossessProperty));
                            Console.WriteLine("Claim added {0}", s);
                      }
                   evaluationContext.AddClaimSet(this,
                           new DefaultClaimSet(this.Issuer,claims));
                   customstate.ClaimsAdded = true;
                   bRet = true;
                }
         else
         {
              bRet = true;
         }
         return bRet;
     }
...
}

Le code précédent montre comment la Evaluate(EvaluationContext, Object) méthode vérifie qu’aucune nouvelle revendication n’a été ajoutée qui affecte le traitement et ajoute des revendications spécifiques. Les revendications autorisées sont obtenues à partir de la GetAllowedOpList méthode, qui est implémentée pour retourner une liste spécifique d’opérations que l’utilisateur est autorisé à effectuer. La politique d’autorisation ajoute des déclarations pour permettre l’accès à une opération spécifique. Cela est utilisé ultérieurement par le ServiceAuthorizationManager pour effectuer des décisions de vérification d'accès.

Une fois le composant personnalisé IAuthorizationPolicy implémenté, l’hôte de service doit être informé des politiques d’autorisation à utiliser.

<serviceAuthorization>
       <authorizationPolicies>
            <add policyType='Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary' />
       </authorizationPolicies>
</serviceAuthorization>

Lorsque vous exécutez l’exemple, les demandes et réponses de l’opération s’affichent dans la fenêtre de la console cliente. Le client appelle correctement les méthodes Add, Soustraction et Multiple et obtient un message « Accès refusé » lors de la tentative d’appel de la méthode Divide. Appuyez sur Entrée dans la fenêtre du client pour arrêter le client.

Configurer le fichier Batch

Le fichier batch Setup.bat inclus dans cet exemple vous permet de configurer le serveur avec des certificats pertinents pour exécuter une application auto-hébergée qui nécessite une sécurité basée sur les certificats du serveur.

Voici une brève vue d’ensemble des différentes sections des fichiers batch afin qu’ils puissent être modifiés pour s’exécuter dans la configuration appropriée :

  • Création du certificat de serveur.

    Les lignes suivantes du fichier batch Setup.bat créent le certificat de serveur à utiliser. La variable %SERVER_NAME% spécifie le nom du serveur. Modifiez cette variable pour spécifier votre propre nom de serveur. La valeur par défaut est localhost.

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
    
  • Installation du certificat de serveur dans le magasin de certificats approuvé du client.

    Les lignes suivantes du fichier batch Setup.bat copient le certificat de serveur dans le répertoire des contacts de confiance du client. Cette étape est requise, car les certificats générés par Makecert.exe ne sont pas implicitement approuvés par le système client. Si vous disposez déjà d’un certificat rooté dans un certificat racine approuvé client( par exemple, un certificat émis par Microsoft), cette étape de remplissage du magasin de certificats client avec le certificat de serveur n’est pas nécessaire.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Création du certificat client.

    Les lignes suivantes du fichier batch Setup.bat créent le certificat client à utiliser. La variable %USER_NAME% spécifie le nom du serveur. Cette valeur est définie sur « test1 » car c'est le nom que la IAuthorizationPolicy recherche. Si vous modifiez la valeur de %USER_NAME% vous devez modifier la valeur correspondante dans la IAuthorizationPolicy.Evaluate méthode.

    Le certificat est stocké dans le magasin My (Personnel) sous l’emplacement du magasin CurrentUser.

    echo ************
    echo making client cert
    echo ************
    makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe
    
  • Installation du certificat client dans le magasin de certificats approuvé du serveur.

    Les lignes suivantes du fichier de commandes Setup.bat copient le certificat client dans le magasin de personnes de confiance du client. Cette étape est requise, car les certificats générés par Makecert.exe ne sont pas implicitement approuvés par le système serveur. Si vous disposez déjà d’un certificat rooté dans un certificat racine approuvé( par exemple, un certificat émis par Microsoft), cette étape de remplissage du magasin de certificats serveur avec le certificat client n’est pas nécessaire.

    certmgr.exe -add -r CurrentUser -s My -c -n %CLIENT_NAME% -r LocalMachine -s TrustedPeople
    

Pour configurer et compiler l’exemple

  1. Pour générer la solution, suivez les instructions indiquées dans la rubrique Génération des exemples Windows Communication Foundation.

  2. Pour exécuter l’exemple dans une configuration à ordinateur unique ou inter-ordinateur, utilisez les instructions suivantes.

Remarque

Si vous utilisez Svcutil.exe pour régénérer la configuration de cet exemple, veillez à modifier le nom du point de terminaison dans la configuration du client pour qu’il corresponde au code client.

Pour exécuter l’exemple sur le même ordinateur

  1. Ouvrez l’invite de commandes du développeur pour Visual Studio avec des privilèges d’administrateur et exécutez Setup.bat à partir de l’exemple de dossier d’installation. Cela installe tous les certificats requis pour l’exécution de l’exemple.

    Remarque

    Le fichier de commandes Setup.bat est conçu pour être exécuté à partir d’une invite de commandes développeur pour Visual Studio. La variable d’environnement PATH définie dans l’invite de commandes développeur pour Visual Studio pointe vers le répertoire qui contient des exécutables requis par le script Setup.bat .

  2. Lancez Service.exe à partir de service\bin.

  3. Lancez Client.exe à partir de \client\bin. L’activité du client s’affiche sur l’application console cliente.

Si le client et le service ne sont pas en mesure de communiquer, consultez Conseils de résolution des problèmes pour les exemples WCF.

Pour exécuter l’exemple sur différents ordinateurs

  1. Créez un répertoire sur l’ordinateur de service.

  2. Copiez les fichiers du programme de service depuis \service\bin vers le répertoire de l’ordinateur de service. Copiez également les fichiers Setup.bat, Cleanup.bat, GetComputerName.vbs et ImportClientCert.bat sur l’ordinateur de service.

  3. Créez un répertoire sur l’ordinateur client pour les fichiers binaires clients.

  4. Copiez les fichiers de programme client dans le répertoire client sur l’ordinateur client. Copiez également les fichiers Setup.bat, Cleanup.batet ImportServiceCert.bat sur le client.

  5. Sur le serveur, exécutez setup.bat service à partir d’une invite de commandes développeur pour Visual Studio ouverte avec des privilèges d’administrateur.

    L’exécution setup.bat avec l’argument service crée un certificat de service avec le nom de domaine complet de l’ordinateur et exporte le certificat de service vers un fichier nommé Service.cer.

  6. Modifiez Service.exe.config pour refléter le nouveau nom de certificat (dans l’attribut findValue du <serviceCertificate>) qui est le même que le nom de domaine complet de l’ordinateur. Remplacez également le nom d’ordinateur dans l’élément <service>/<baseAddresses> de localhost par le nom complet de votre ordinateur de service.

  7. Copiez le fichier Service.cer du répertoire de service vers le répertoire client sur l’ordinateur client.

  8. Sur le client, exécutez setup.bat client à partir d’une invite de commandes développeur pour Visual Studio ouverte avec des privilèges d’administrateur.

    L’exécution setup.bat avec l’argument client crée un certificat client nommé test1 et exporte le certificat client vers un fichier nommé Client.cer.

  9. Dans le fichier Client.exe.config sur l’ordinateur client, modifiez la valeur d’adresse du point de terminaison pour qu’il corresponde à la nouvelle adresse de votre service. Pour ce faire, remplacez localhost par le nom de domaine complet du serveur.

  10. Copiez le fichier Client.cer du répertoire client vers le répertoire de service sur le serveur.

  11. Sur le client, exécutez ImportServiceCert.bat à partir d’une invite de commandes développeur pour Visual Studio ouverte avec des privilèges d’administrateur.

    Cette opération importe le certificat de service du fichier Service.cer vers le magasin CurrentUser - TrustedPeople.

  12. Sur le serveur, exécutez ImportClientCert.bat à partir d’une invite de commandes développeur pour Visual Studio ouverte avec des privilèges d’administrateur.

    Cela importe le certificat client à partir du fichier Client.cer dans le magasin LocalMachine - TrustedPeople .

  13. Sur l’ordinateur serveur, lancez Service.exe à partir de la fenêtre d’invite de commandes.

  14. Sur l’ordinateur client, lancez Client.exe à partir d’une fenêtre d’invite de commandes.

    Si le client et le service ne sont pas en mesure de communiquer, consultez Conseils de résolution des problèmes pour les exemples WCF.

Nettoyer après l’échantillon

Pour nettoyer après l’exemple, exécutez Cleanup.bat dans le dossier d’exemples lorsque vous avez terminé d’exécuter l’exemple. Cela supprime le serveur et les certificats clients du magasin de certificats.

Remarque

Ce script ne supprime pas les certificats de service sur un client lors de l’exécution de cet exemple sur les ordinateurs. Si vous avez exécuté des exemples WCF qui utilisent des certificats sur plusieurs ordinateurs, veillez à effacer les certificats de service installés dans le magasin CurrentUser - TrustedPeople. Pour ce faire, utilisez la commande suivante : certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Par exemple : certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.