Compartilhar via


Política de autorização

Esta amostra demonstra como implementar uma política de autorização de declaração personalizada e um gerenciador de autorização de serviço personalizado associado. É útil quando o serviço faz as verificações de acesso baseadas em declarações para as operações de serviço e, antes das verificações de acesso, concede determinados direitos ao chamador. Esta amostra ilustra tanto o processo de adição de declarações quanto o processo para fazer uma verificação de acesso em relação ao conjunto finalizado de declarações. Todas as mensagens de aplicativo entre o cliente e o servidor são assinadas e criptografadas. Por padrão, com a associação wsHttpBinding, são usados um nome de usuário e uma senha, fornecidos pelo cliente, para fazer logon em uma conta válida do Windows. Esta amostra demonstra como utilizar um UserNamePasswordValidator personalizado para autenticar o cliente. Além disso, esta amostra mostra o cliente se autenticando no serviço usando um certificado X.509. Esta amostra demonstra a implementação de IAuthorizationPolicy e ServiceAuthorizationManager que, entre eles, concede acesso a métodos específicos do serviço para usuários específicos. Esta amostra é baseada no Nome do Usuário de Segurança de Mensagem, mas demonstra como executar uma transformação de declaração antes de ServiceAuthorizationManager ser chamado.

Observação

O procedimento de instalação e as instruções de compilação dessa amostra estão no final deste tópico.

Em resumo, esta amostra demonstra como:

  • O cliente pode ser autenticado usando um nome de usuário e senha.

  • O cliente pode ser autenticado usando um certificado X.509.

  • O servidor valida as credenciais do cliente em relação a um validador UsernamePassword personalizado.

  • O servidor é autenticado usando o certificado X.509 do servidor.

  • O servidor pode usar ServiceAuthorizationManager para controlar o acesso a determinados métodos no serviço.

  • Como implementar IAuthorizationPolicy.

O serviço expõe dois pontos de extremidade para comunicação com o serviço, definidos usando o arquivo de configuração App.config. Cada ponto de extremidade consiste em um endereço, uma associação e um contrato. Uma associação é configurada com uma associação padrão wsHttpBinding que usa WS-Security e autenticação de nome de usuário do cliente. A outra associação é configurada com uma associação padrão wsHttpBinding que usa WS-Security e autenticação de certificado do cliente. O <comportamento> especifica que as credenciais do usuário devem ser usadas para autenticação de serviço. O certificado do servidor deve conter o mesmo valor da propriedade SubjectName que o atributo findValue no <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>

A configuração do ponto de extremidade do cliente consiste em um nome de configuração, um endereço absoluto para o ponto de extremidade de serviço, a associação e o contrato. A associação de cliente é configurada com o modo de segurança apropriado, como especificado nesse caso na <segurança> e em clientCredentialType como especificado na <mensagem>.

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

Para o ponto de extremidade baseado em nome de usuário, a implementação do cliente define o nome de usuário e a senha a serem usados.

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

Para o ponto de extremidade baseado em certificado, a implementação do cliente define o certificado do cliente a ser usado.

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

Esta amostra usa um UserNamePasswordValidator personalizado para validar nomes de usuário e senhas. A amostra implementa MyCustomUserNamePasswordValidator derivado de UserNamePasswordValidator. Veja a documentação sobre UserNamePasswordValidator para obter mais informações. Para demonstrar a integração com o UserNamePasswordValidator, esta amostra de validador personalizado implementa o método Validate para aceitar pares de nomes de usuário/senhas em que o nome de usuário corresponde à senha, conforme exibido no código a seguir.

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

Depois que o validador for implementado no código de serviço, o host de serviço deve ser informado sobre a instância de validador a ser usada. Isso é feito usando o seguinte código:

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

Ou você pode fazer a mesma coisa na configuração:

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

O WCF (Windows Communication Foundation) fornece um modelo avançado baseado em declarações para executar verificações de acesso. O objeto ServiceAuthorizationManager é usado para executar a verificação de acesso e determinar se as declarações associadas ao cliente atendem aos requisitos necessários para acessar o método de serviço.

Para fins de demonstração, esta amostra demonstra uma implementação de ServiceAuthorizationManager com o método CheckAccessCore para permitir o acesso de um usuário a métodos com base em declarações do tipo http://example.com/claims/allowedoperation cujo valor é o URI de Ação da operação que tem permissão para ser chamada.

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

Depois que o ServiceAuthorizationManager personalizado for implementado, o host de serviço deverá ser informado sobre o ServiceAuthorizationManager a ser usado. Isso é feito como mostrado no código a seguir.

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

O método principal IAuthorizationPolicy a ser implementado é o método 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;
     }
...
}

O código anterior mostra como o método Evaluate(EvaluationContext, Object) verifica se nenhuma declaração nova foi adicionada de forma a afetar o processamento e adiciona declarações específicas. As declarações permitidas são obtidas do método GetAllowedOpList, que é implementado para retornar uma lista específica de operações que o usuário tem permissão para executar. A política de autorização adiciona declarações para acessar a operação específica. Isso é usado posteriormente por ServiceAuthorizationManager para executar decisões de verificação de acesso.

Depois que o IAuthorizationPolicy personalizado for implementado, o host de serviço deverá ser informado sobre as políticas de autorização a serem usadas.

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

Quando você executa a amostra, as solicitações de operação e as respostas são exibidas na janela do console do cliente. O cliente chama com êxito os métodos Add, Subtract e Multiple e recebe a mensagem "Acesso negado" ao tentar chamar o método Divide. Pressione ENTER na janela do cliente para desligá-lo.

Arquivo de configuração em lote

O arquivo Setup.bat em lote, incluído com esta amostra, permite que você configure o servidor com os certificados relevantes para executar um aplicativo auto-hospedado que exige segurança baseada em certificado do servidor.

A seguir está breve visão geral das diferentes seções dos arquivos em lote que podem ser modificados para executar a configuração apropriada:

  • Criação do certificado do servidor.

    As linhas a seguir do arquivo em lote Setup.bat criam o certificado do servidor a ser usado. A variável %SERVER_NAME% especifica o nome do servidor. Altere essa variável para especificar o nome do seu próprio servidor. O valor padrão é 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
    
  • Instalação do certificado do servidor no repositório de certificados confiáveis do cliente.

    As linhas a seguir no arquivo em lote Setup.bat copiam o certificado do servidor no repositório de pessoas confiáveis do cliente. Essa etapa é necessária porque os certificados gerados por Makecert.exe não são implicitamente confiáveis para o sistema do cliente. Se você já tiver um certificado com raiz em um certificado raiz confiável do cliente, por exemplo, um certificado emitido pela Microsoft, essa etapa de preenchimento do repositório de certificados do cliente com o certificado do servidor não será necessária.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Criação do certificado do cliente.

    As linhas a seguir do arquivo em lote Setup.bat criam o certificado do cliente a ser usado. A variável %USER_NAME% especifica o nome do servidor. Esse valor é definido como "test1" porque esse é o nome que a IAuthorizationPolicy procura. Se você alterar o valor de %USER_NAME%, também deverá alterar o valor correspondente no método IAuthorizationPolicy.Evaluate.

    O certificado é armazenado no repositório Meu (Pessoal) no local do repositório CurrentUser.

    echo ************
    echo making client cert
    echo ************
    makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe
    
  • Instalação do certificado do cliente no repositório de certificados confiáveis do servidor.

    As linhas a seguir no arquivo em lote Setup.bat copiam o certificado do servidor no repositório de pessoas confiáveis do cliente. Essa etapa é necessária porque os certificados gerados por Makecert.exe não são implicitamente confiáveis para o sistema do servidor. Se você já tiver um certificado com raiz em um certificado raiz confiável do cliente, como um certificado emitido pela Microsoft, por exemplo, essa etapa de preenchimento do repositório de certificados do servidor com certificados do servidor não será necessária.

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

Para configurar e compilar a amostra

  1. Para compilar a solução, siga as instruções contidas em Compilar as amostras do Windows Communication Foundation.

  2. Para executar a amostra em uma configuração de computador único ou entre computadores, use as instruções a seguir.

Observação

Se você usar Svcutil.exe para regenerar a configuração desta amostra, modifique o nome do ponto de extremidade na configuração do cliente para corresponder ao código do cliente.

Para executar a amostra no mesmo computador

  1. Abra o Prompt de Comando do Desenvolvedor para Visual Studio com privilégios de administrador e execute Setup.bat na pasta de instalação da amostra. Isso instalará todos os certificados necessários para executar a amostra.

    Observação

    O arquivo em lote Setup.bat foi projetado para ser executado no Prompt de Comando do Desenvolvedor para Visual Studio. A variável de ambiente PATH definida no Prompt de Comando do Desenvolvedor do Visual Studio indica o diretório que contém executáveis exigidos pelo script Setup.bat.

  2. Inicialize o Service.exe em service\bin.

  3. Inicialize o Client.exe em \client\bin. A atividade do cliente é exibida no aplicativo do console do cliente.

Se o cliente e o serviço não puderem se comunicar, confira Dicas de solução de problemas para exemplos de WCF.

Para executar a amostra em vários computadores

  1. Criar um diretório no computador de serviço.

  2. Copie os arquivos do programa de serviço de \service\bin para o diretório no computador de serviço. Copie também os arquivos Setup.bat, Cleanup.bat, GetComputerName.vbs e ImportClientCert.bat para o computador de serviço.

  3. Crie um diretório no computador cliente para os binários do cliente.

  4. Copie os arquivos do programa do cliente para o diretório do cliente no computador cliente. Copie também os arquivos Setup.bat, Cleanup.bat e ImportServiceCert.bat para o cliente.

  5. No servidor, execute setup.bat service em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

    A execução de setup.bat com o argumento service cria um certificado de serviço com o nome de domínio totalmente qualificado do computador e exporta o certificado de serviço para o arquivo Service.cer.

  6. Edite o Service.exe.config para que ele reflita o novo nome do certificado (no atributo findValue no <serviceCertificate>), que é o mesmo que o nome de domínio totalmente qualificado do computador. Altere também o computername no elemento <service>/<baseAddresses> do localhost para o nome totalmente qualificado do computador de serviço.

  7. Copie o arquivo Service.cer do diretório de serviço para o diretório do cliente no computador cliente.

  8. No cliente, execute setup.bat client em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

    A execução de setup.bat com o argumento client cria e exporta o certificado de cliente test1 para o arquivo Client.cer.

  9. No arquivo Client.exe.config do computador cliente, altere o valor do endereço do ponto de extremidade para que corresponda ao novo endereço do seu serviço. Faça isso substituindo o localhost pelo nome de domínio totalmente qualificado do servidor.

  10. Copie o arquivo Client.cer do diretório do cliente para o diretório de serviço no servidor.

  11. No cliente, execute ImportServiceCert.bat em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

    Isso importará o certificado de serviço do arquivo Service.cer para o repositório CurrentUser – TrustedPeople.

  12. No servidor, execute ImportClientCert.bat em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

    Isso importará o certificado do cliente do arquivo Client.cer para o repositório LocalMachine – TrustedPeople.

  13. No computador do servidor, inicialize Service.exe na janela do prompt de comando.

  14. No computador cliente, inicialize Client.exe na janela do prompt de comando.

    Se o cliente e o serviço não puderem se comunicar, confira as Dicas de solução de problemas para amostras de WCF.

Limpar após a amostra

Para limpar após a amostra, execute Cleanup.bat na pasta de amostras quando terminar de executar a amostra. Dessa forma, os certificados do servidor e do cliente são removidos do repositório de certificados.

Observação

Esse script não remove os certificados de serviço em um cliente na execução dessa amostra em vários computadores. Se você tiver executado amostras do WCF (Windows Communication Foundation) que usam certificados em vários computadores, lembre-se de limpar os certificados de serviço instalados no repositório CurrentUser – TrustedPeople. Para isso, use o seguinte comando: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Por exemplo: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.