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.

Kommentar

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 autentisering med WS-Security och klientens användarnamn. 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 egenskapen som attributet i< serviceCertificate>.findValueSubjectName

<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 . I syfte att demonstrera integreringen UserNamePasswordValidatormed implementerar Validate det här exemplet för anpassad validering metoden för att acceptera användarnamn/lösenordspar där användarnamnet matchar lösenordet enligt 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 det här exemplet en implementering av ServiceAuthorizationManager som CheckAccessCore implementerar metoden för att tillåta en användares åtkomst till metoder baserat på anspråk av typen http://example.com/claims/allowedoperation vars värde är åtgärds-URI för den åtgärd som tillåts anropas.

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 hur den ServiceAuthorizationManager 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 för att utföra beslut om ServiceAuthorizationManager å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. Klienten anropar metoderna Lägg till, Subtrahera och Flera och får meddelandet "Åtkomst nekas" när du 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 klientarkivet för betrodda personer. 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 servernamnet. 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 arkivet My (Personal) under arkivplatsen 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 batchfil kopierar klientcertifikatet till arkivet betrodda personer. 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.

Kommentar

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.

    Kommentar

    Den Setup.bat batchfilen är utformad för att köras från Developer Command Prompt 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 filerna Setup.bat, Cleanup.bat och ImportServiceCert.bat 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 service kör setup.bat med 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 findValue attributet 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.

    När du client kör setup.bat med argumentet skapas ett klientcertifikat med namnet test1 och klientcertifikatet exporteras till en fil med namnet Client.cer.

  9. I filen Client.exe.config 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 du ImportServiceCert.bat i Kommandotolken för utvecklare för Visual Studio som öppnats med administratörsbehörighet.

    Detta importerar tjänstcertifikatet från filen Service.cer till arkivet CurrentUser – Trusted Personer.

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

    Detta importerar klientcertifikatet från filen Client.cer till arkivet LocalMachine – Trusted Personer.

  13. Starta Service.exe från kommandotolken på serverdatorn.

  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

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

Kommentar

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-exempel som använder certifikat mellan datorer måste du rensa de tjänstcertifikat som har installerats i arkivet CurrentUser – Trusted Personer. 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.