Dela via


Auktoriseringsprincip

Det här exemplet visar hur du implementerar en princip för anpassad anspråksauktorisering och en associerad hanterare för anpassad tjänstauktorisering. Detta är användbart när tjänsten gör anspråksbaserade åtkomstkontroller till tjänståtgärder och före åtkomstkontrollerna ger anroparen vissa rättigheter. Det här exemplet visar både processen för att lägga till anspråk och processen för att göra en åtkomstkontroll mot den slutförda uppsättningen anspråk. Alla programmeddelanden mellan klienten och servern är signerade och krypterade. Som standard med bindningen wsHttpBinding används ett användarnamn och lösenord som tillhandahålls av klienten för att logga in på ett giltigt Windows-konto. Det här exemplet visar hur du använder en anpassad UserNamePasswordValidator för att autentisera klienten. Dessutom visar det här exemplet klienten som autentiserar till tjänsten med hjälp av ett X.509-certifikat. Det här exemplet visar en implementering av IAuthorizationPolicy och ServiceAuthorizationManager, som mellan dem beviljar åtkomst till specifika metoder för tjänsten för specifika användare. Det här exemplet baseras på användarnamnet för meddelandesäkerhet, men visar hur du utför en anspråkstransformering innan det ServiceAuthorizationManager anropas.

Anmärkning

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av det här avsnittet.

Sammanfattningsvis visar det här exemplet hur:

  • Klienten kan autentiseras med ett användarnamn-lösenord.

  • Klienten kan autentiseras med ett X.509-certifikat.

  • Servern validerar klientautentiseringsuppgifterna mot en anpassad UsernamePassword validator.

  • Servern autentiseras med hjälp av serverns X.509-certifikat.

  • Servern kan använda ServiceAuthorizationManager för att styra åtkomsten till vissa metoder i tjänsten.

  • Så här implementerar IAuthorizationPolicydu .

Tjänsten exponerar två slutpunkter för kommunikation med tjänsten, som definieras med hjälp av konfigurationsfilen App.config. Varje slutpunkt består av en adress, en bindning och ett kontrakt. En bindning konfigureras med en standardbindning wsHttpBinding som använder WS-Security- och klientanvändarautentisering. Den andra bindningen konfigureras med en standardbindning wsHttpBinding som använder WS-Security- och klientcertifikatautentisering. Beteendet <> anger att användarautentiseringsuppgifterna ska användas för tjänstautentisering. Servercertifikatet måste innehålla samma värde för SubjectName egenskapen som findValue attributet i <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>

Varje klientslutpunktskonfiguration består av ett konfigurationsnamn, en absolut adress för tjänstslutpunkten, bindningen och kontraktet. Klientbindningen konfigureras med lämpligt säkerhetsläge enligt vad som anges i det här fallet i <säkerheten> och clientCredentialType enligt vad som anges i <meddelandet>.

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

För den användarnamnsbaserade slutpunkten anger klientimplementeringen det användarnamn och lösenord som ska användas.

// 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();

För den certifikatbaserade slutpunkten anger klientimplementeringen att klientcertifikatet ska användas.

// 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();

Det här exemplet använder en anpassad UserNamePasswordValidator för att verifiera användarnamn och lösenord. Exemplet implementerar MyCustomUserNamePasswordValidator, härlett från UserNamePasswordValidator. Mer information finns i dokumentationen om UserNamePasswordValidator . För att demonstrera integrationen med UserNamePasswordValidator, implementerar denna anpassade validator ett prov på Validate-metoden för att godkänna användarnamn/lösenordspar där användarnamnet stämmer överens med lösenordet, som visas i följande kod.

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");
    }
  }
}

När validatorn har implementerats i tjänstkoden måste tjänstvärden informeras om den validatorinstans som ska användas. Detta görs med hjälp av följande kod:

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

Eller så kan du göra samma sak i konfigurationen:

<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) tillhandahåller en omfattande anspråksbaserad modell för att utföra åtkomstkontroller. Objektet ServiceAuthorizationManager används för att utföra åtkomstkontrollen och avgöra om anspråken som är associerade med klienten uppfyller de krav som krävs för att få åtkomst till tjänstmetoden.

I demonstrationssyfte visar detta exempel en implementation av ServiceAuthorizationManager som använder CheckAccessCore metoden för att tillåta användaråtkomst till metoder baserat på anspråk av typen http://example.com/claims/allowedoperation, vars värde är handlings-URI:n för den operation som är tillåten att anropa.

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;
  }
}

När den anpassade ServiceAuthorizationManager har implementerats måste tjänstvärden informeras om vilken ServiceAuthorizationManager som ska användas. Detta görs enligt följande kod.

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

Den primära IAuthorizationPolicy metoden som ska implementeras Evaluate(EvaluationContext, Object) är metoden.

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

Föregående kod visar hur Evaluate(EvaluationContext, Object) metoden kontrollerar att inga nya anspråk har lagts till som påverkar bearbetningen och lägger till specifika anspråk. De anspråk som tillåts hämtas från GetAllowedOpList metoden, som implementeras för att returnera en specifik lista över åtgärder som användaren tillåts utföra. Auktoriseringsprincipen lägger till anspråk för åtkomst till den specifika åtgärden. Detta används senare av ServiceAuthorizationManager för att fatta beslut om åtkomstkontroll.

När den anpassade IAuthorizationPolicy har implementerats måste tjänstvärden informeras om de auktoriseringsprinciper som ska användas.

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

När du kör exemplet visas åtgärdsbegäranden och svar i klientkonsolfönstret. Kunden anropar metoderna Lägg till, Subtrahera och Multiplicera och får ett meddelande "Åtkomst nekas" när kunden försöker anropa metoden Dividera. Tryck på RETUR i klientfönstret för att stänga av klienten.

Konfigurera Batch-fil

Med Setup.bat batchfil som ingår i det här exemplet kan du konfigurera servern med relevanta certifikat för att köra ett program med egen värd som kräver servercertifikatbaserad säkerhet.

Följande ger en kort översikt över de olika avsnitten i batchfilerna så att de kan ändras så att de körs i rätt konfiguration:

  • Skapa servercertifikatet.

    Följande rader från Setup.bat batchfil skapar det servercertifikat som ska användas. Variabeln %SERVER_NAME% anger servernamnet. Ändra den här variabeln för att ange ditt eget servernamn. Standardvärdet är 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
    
  • Installera servercertifikatet i klientens betrodda certifikatarkiv.

    Följande rader i Setup.bat batchfil kopierar servercertifikatet till klientens betrodda personers lagring. Det här steget krävs eftersom certifikat som genereras av Makecert.exe inte är implicit betrodda av klientsystemet. Om du redan har ett certifikat som är rotat i ett klientbetrott rotcertifikat, till exempel ett Microsoft-utfärdat certifikat, krävs inte det här steget för att fylla i klientcertifikatarkivet med servercertifikatet.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Skapa klientcertifikatet.

    Följande rader från Setup.bat batchfil skapar det klientcertifikat som ska användas. Variabeln %USER_NAME% anger servernamn. Det här värdet är inställt på "test1" eftersom det här är namnet som söks IAuthorizationPolicy efter. Om du ändrar värdet för %USER_NAME% måste du ändra motsvarande värde i IAuthorizationPolicy.Evaluate metoden.

    Certifikatet lagras i butiken My (Personal) under lagringsplatsen CurrentUser.

    echo ************
    echo making client cert
    echo ************
    makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe
    
  • Installera klientcertifikatet i serverns betrodda certifikatarkiv.

    Följande rader i Setup.bat batchfilen kopierar klientcertifikatet till det betrodda förrådet. Det här steget krävs eftersom certifikat som genereras av Makecert.exe inte är implicit betrodda av serversystemet. Om du redan har ett certifikat som är rotat i ett betrott rotcertifikat, till exempel ett Microsoft-utfärdat certifikat, krävs inte det här steget för att fylla i servercertifikatarkivet med klientcertifikatet.

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

Så här konfigurerar och skapar du exemplet

  1. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

  2. Om du vill köra exemplet i en konfiguration med en eller flera datorer använder du följande instruktioner.

Anmärkning

Om du använder Svcutil.exe för att återskapa konfigurationen för det här exemplet måste du ändra slutpunktsnamnet i klientkonfigurationen så att det matchar klientkoden.

Så här kör du exemplet på samma dator

  1. Öppna Kommandotolken för utvecklare för Visual Studio med administratörsbehörighet och kör Setup.bat från exempelinstallationsmappen. Detta installerar alla certifikat som krävs för att köra exemplet.

    Anmärkning

    Batchfilen Setup.bat är utformad för att köras från Utvecklarkommandotolken för Visual Studio. Variabeln PATH-miljö som anges i Kommandotolken för utvecklare för Visual Studio pekar på katalogen som innehåller körbara filer som krävs av Setup.bat skriptet.

  2. Starta Service.exe från service\bin.

  3. Starta Client.exe från \client\bin. Klientaktiviteten visas i klientkonsolprogrammet.

Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Så här kör du exemplet mellan datorer

  1. Skapa en katalog på tjänstdatorn.

  2. Kopiera tjänstprogramfilerna från \service\bin till katalogen på tjänstdatorn. Kopiera även filerna Setup.bat, Cleanup.bat, GetComputerName.vbs och ImportClientCert.bat till tjänstdatorn.

  3. Skapa en katalog på klientdatorn för klient binärfilerna.

  4. Kopiera klientprogramfilerna till klientkatalogen på klientdatorn. Kopiera även Setup.bat, Cleanup.batoch ImportServiceCert.bat filer till klienten.

  5. På servern kör du setup.bat service i Kommandotolken för utvecklare för Visual Studio som öppnats med administratörsbehörighet.

    När du kör setup.bat med service argumentet skapas ett tjänstcertifikat med datorns fullständigt kvalificerade domännamn, och tjänstcertifikatet exporteras till en fil med namnet Service.cer.

  6. Redigera Service.exe.config för att återspegla det nya certifikatnamnet (i findValueattributet i< serviceCertificate>) som är samma som datorns fullständigt kvalificerade domännamn. Ändra även datornamnet i <service>/<baseAddresses-elementet> från localhost till det fullständigt kvalificerade namnet på tjänstdatorn.

  7. Kopiera Service.cer-filen från tjänstkatalogen till klientkatalogen på klientdatorn.

  8. På klienten kör du setup.bat client i Kommandotolken för utvecklare för Visual Studio som öppnats med administratörsbehörighet.

    Kör setup.bat med argumentet client för att skapa ett klientcertifikat med namnet test1 och exportera klientcertifikatet till en fil med namnet Client.cer.

  9. I denClient.exe.config filen på klientdatorn ändrar du slutpunktens adressvärde så att det matchar tjänstens nya adress. Gör detta genom att ersätta localhost med serverns fullständigt kvalificerade domännamn.

  10. Kopiera Client.cer-filen från klientkatalogen till tjänstkatalogen på servern.

  11. På klienten, kör ImportServiceCert.bat i Utvecklarkommandotolken för Visual Studio som öppnats med administratörsbehörighet.

    Detta importerar tjänstcertifikatet från filen Service.cer till lagringsplatsen CurrentUser – TrustedPeople.

  12. På servern öppnar du Kommandotolken för utvecklare i Visual Studio med administratörsbehörighet och kör ImportClientCert.bat.

    Detta importerar klientcertifikatet från filen Client.cer till lagringsplatsen LocalMachine – TrustedPeople.

  13. På serverdatorn, starta Service.exe från kommandotolken.

  14. Starta Client.exe från kommandotolken på klientdatorn.

    Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Rensa efter exemplet

För att rensa efter exemplet, kör Cleanup.bat i exempelmappen när du har kört klart exemplet. Detta tar bort server- och klientcertifikaten från certifikatarkivet.

Anmärkning

Det här skriptet tar inte bort tjänstcertifikat på en klient när du kör det här exemplet på flera datorer. Om du har kört WCF-exempelprogram som använder certifikat mellan datorer, se till att rensa de tjänstcertifikat som har installerats i lagringsplatsen CurrentUser – TrustedPeople. Det gör du genom att använda följande kommando: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Till exempel: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.