Compartilhar via


Autenticador de token

Este exemplo demonstra como implementar um autenticador de token personalizado. Um autenticador de token no WCF (Windows Communication Foundation) é usado para validar o token usado com a mensagem, verificar se ele é auto-consistente e autenticar a identidade associada ao token.

Autenticadores de token personalizados são úteis em uma variedade de casos, como:

  • Quando você deseja substituir o mecanismo de autenticação padrão associado a um token.

  • Quando você estiver criando um token personalizado.

Este exemplo demonstra o seguinte:

  • Como um cliente pode se autenticar usando um par de nome de usuário/senha.

  • Como o servidor pode validar as credenciais do cliente usando um autenticador de token personalizado.

  • Como o código do serviço WCF se vincula ao autenticador de token personalizado.

  • Como o servidor pode ser autenticado usando o certificado X.509 do servidor.

Este exemplo também mostra como a identidade do chamador é acessível do WCF após o processo de autenticação de token personalizado.

O serviço expõe um único endpoint para comunicação com o serviço, definido usando o arquivo de configuração App.config. O endpoint consiste em um endereço, uma vinculação e um contrato. A associação é configurada com um padrão wsHttpBinding, com o modo de segurança definido como mensagem - o modo padrão do wsHttpBinding. Este exemplo define o padrão wsHttpBinding para usar a autenticação de nome de usuário do cliente. O serviço também configura o certificado de serviço usando o comportamento serviceCredentials. O securityCredentials comportamento permite que você especifique um certificado de serviço. Um certificado de serviço é usado por um cliente para autenticar o serviço e fornecer proteção de mensagem. A configuração a seguir faz referência ao certificado localhost instalado durante a configuração de exemplo, conforme descrito nas instruções de instalação a seguir.

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

    <bindings>
      <wsHttpBinding>
        <binding name="Binding1">
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="False" />
          <!--
          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.
....        -->
          <serviceCredentials>
            <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
          </serviceCredentials>
        </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 Mode e clientCredentialType apropriados.

<system.serviceModel>
    <client>
      <endpoint name=""
                address="http://localhost:8000/servicemodelsamples/service"
                binding="wsHttpBinding"
                bindingConfiguration="Binding1"
                contract="Microsoft.ServiceModel.Samples.ICalculator">
      </endpoint>
    </client>

    <bindings>
      <wsHttpBinding>
        <binding name="Binding1">
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

A implementação do cliente define o nome de usuário e a senha a serem usados.

static void Main()
{
     ...
     client.ClientCredentials.UserNamePassword.UserName = username;
     client.ClientCredentials.UserNamePassword.Password = password;
     ...
}

Autenticador de Token Personalizado

Use as seguintes etapas para criar um autenticador de token personalizado:

  1. Escreva um autenticador de token personalizado.

    O exemplo implementa um autenticador de token personalizado que valida que o nome de usuário tem um formato de email válido. Ele deriva de UserNameSecurityTokenAuthenticator. O método mais importante nesta classe é ValidateUserNamePasswordCore(String, String). Nesse método, o autenticador valida o formato do nome de usuário e também que o nome de host não é de um domínio invasor. Se ambas as condições forem atendidas, ele retornará uma coleção somente leitura de IAuthorizationPolicy instâncias usada para fornecer declarações que representam as informações armazenadas no token de nome de usuário.

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName, string password)
    {
        if (!ValidateUserNameFormat(userName))
            throw new SecurityTokenValidationException("Incorrect UserName format");
    
        ClaimSet claimSet = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userName, Rights.PossessProperty));
        List<IIdentity> identities = new List<IIdentity>(1);
        identities.Add(new GenericIdentity(userName));
        List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
        policies.Add(new UnconditionalPolicy(ClaimSet.System, claimSet, DateTime.MaxValue.ToUniversalTime(), identities));
        return policies.AsReadOnly();
    }
    
  2. Forneça uma política de autorização retornada pelo autenticador de token personalizado.

    Este exemplo fornece sua própria implementação de IAuthorizationPolicy chamada UnconditionalPolicy que retorna um conjunto de declarações e identidades que foram passadas para ele em seu construtor.

    class UnconditionalPolicy : IAuthorizationPolicy
    {
        String id = Guid.NewGuid().ToString();
        ClaimSet issuer;
        ClaimSet issuance;
        DateTime expirationTime;
        IList<IIdentity> identities;
    
        public UnconditionalPolicy(ClaimSet issuer, ClaimSet issuance, DateTime expirationTime, IList<IIdentity> identities)
        {
            if (issuer == null)
                throw new ArgumentNullException("issuer");
            if (issuance == null)
                throw new ArgumentNullException("issuance");
    
            this.issuer = issuer;
            this.issuance = issuance;
            this.identities = identities;
            this.expirationTime = expirationTime;
        }
    
        public string Id
        {
            get { return this.id; }
        }
    
        public ClaimSet Issuer
        {
            get { return this.issuer; }
        }
    
        public DateTime ExpirationTime
        {
            get { return this.expirationTime; }
        }
    
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            evaluationContext.AddToTarget(this, this.issuance);
    
            if (this.identities != null)
            {
                object value;
                IList<IIdentity> contextIdentities;
                if (!evaluationContext.Properties.TryGetValue("Identities", out value))
                {
                    contextIdentities = new List<IIdentity>(this.identities.Count);
                    evaluationContext.Properties.Add("Identities", contextIdentities);
                }
                else
                {
                    contextIdentities = value as IList<IIdentity>;
                }
                foreach (IIdentity identity in this.identities)
                {
                    contextIdentities.Add(identity);
                }
            }
    
            evaluationContext.RecordExpirationTime(this.expirationTime);
            return true;
        }
    }
    
  3. Escreva um gerenciador de tokens de segurança personalizado.

    O SecurityTokenManager é usado para criar um SecurityTokenAuthenticator para objetos específicos SecurityTokenRequirement que são passados para ele no CreateSecurityTokenAuthenticator método. O gerenciador de tokens de segurança também é usado para criar provedores de token e serializadores de token, mas eles não são cobertos por este exemplo. Neste exemplo, o gerenciador de tokens de segurança personalizado herda a classe ServiceCredentialsSecurityTokenManager e substitui o método CreateSecurityTokenAuthenticator para retornar um autenticador personalizado de nome de usuário quando os requisitos de token passados indicam que um autenticador de nome de usuário é solicitado.

    public class MySecurityTokenManager : ServiceCredentialsSecurityTokenManager
    {
        MyUserNameCredential myUserNameCredential;
    
        public MySecurityTokenManager(MyUserNameCredential myUserNameCredential)
            : base(myUserNameCredential)
        {
            this.myUserNameCredential = myUserNameCredential;
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement.TokenType ==  SecurityTokenTypes.UserName)
            {
                outOfBandTokenResolver = null;
                return new MyTokenAuthenticator();
            }
            else
            {
                return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
            }
        }
    }
    
  4. Escreva uma credencial de serviço personalizada.

    A classe de credenciais de serviço é usada para representar as credenciais configuradas para o serviço e cria um gerenciador de tokens de segurança que é usado para obter autenticadores de token, provedores de token e serializadores de token.

    public class MyUserNameCredential : ServiceCredentials
    {
    
        public MyUserNameCredential()
            : base()
        {
        }
    
        protected override ServiceCredentials CloneCore()
        {
            return new MyUserNameCredential();
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new MySecurityTokenManager(this);
        }
    
    }
    
  5. Configure o serviço para usar a credencial de serviço personalizada.

    Para que o serviço use a credencial de serviço personalizada, excluimos a classe de credencial de serviço padrão depois de capturar o certificado de serviço que já está pré-configurado na credencial de serviço padrão e configuramos a nova instância de credencial de serviço para usar os certificados de serviço pré-configurados e adicionar essa nova instância de credencial de serviço aos comportamentos de serviço.

    ServiceCredentials sc = serviceHost.Credentials;
    X509Certificate2 cert = sc.ServiceCertificate.Certificate;
    MyUserNameCredential serviceCredential = new MyUserNameCredential();
    serviceCredential.ServiceCertificate.Certificate = cert;
    serviceHost.Description.Behaviors.Remove((typeof(ServiceCredentials)));
    serviceHost.Description.Behaviors.Add(serviceCredential);
    

Para exibir as informações do chamador, você pode usar PrimaryIdentity conforme mostrado no código a seguir. O Current contém informações de declarações sobre o chamador atual.

static void DisplayIdentityInformation()
{
    Console.WriteLine("\t\tSecurity context identity  :  {0}",
            ServiceSecurityContext.Current.PrimaryIdentity.Name);
     return;
}

Quando você executa o exemplo, as solicitações e respostas da operação são exibidas na janela do console do cliente. Pressione ENTER na janela do cliente para desligar o cliente.

Arquivo de configuração em lote

O arquivo em lote Setup.bat incluído com este exemplo permite que você configure o servidor com certificados relevantes para executar um aplicativo hospedado por você mesmo que necessita de segurança baseada em certificado emitido pelo servidor. Esse arquivo em lote precisa ser modificado para funcionar em computadores ou em um caso não hospedado.

A seguir, fornece uma breve visão geral das diferentes seções dos arquivos em lote para que possam ser modificadas para serem executadas na configuração apropriada.

  • Criando o certificado do servidor.

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

    Observação

    O arquivo de lote de instalação foi projetado para ser executado em um Prompt de Comando do SDK do Windows. Isso requer que a variável de ambiente MSSDK aponte para o diretório em que o SDK está instalado. Essa variável de ambiente é definida automaticamente em um Prompt de Comando do SDK do Windows.

Para configurar e compilar o exemplo

  1. Verifique se você executou o Procedimento de instalação avulsa dos exemplos do Windows Communication Foundation.

  2. Para criar a solução, siga as instruções na criação dos exemplos do Windows Communication Foundation.

Para executar o exemplo no mesmo computador

  1. Execute Setup.bat na pasta de instalação de exemplo dentro de um prompt de comando do Visual Studio aberto com privilégios de administrador. Isso instala todos os certificados necessários para executar o exemplo.

    Observação

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

  2. Inicie service.exe do service\bin.

  3. Inicie client.exe de \client\bin. A atividade do cliente é exibida no aplicativo de console do cliente.

  4. 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 o exemplo em vários computadores

  1. Crie um diretório no computador de serviço para os binários de serviço.

  2. Copie os arquivos do programa de serviço para o diretório de serviço no computador de serviço. Copie também os arquivos Setup.bat e Cleanup.bat para o computador de serviço.

  3. Você precisa ter um certificado de servidor com o nome da entidade que contém o nome de domínio totalmente qualificado do computador. O arquivo de App.config de serviço deve ser atualizado para refletir esse novo nome de certificado. Você pode criar um usando o Setup.bat se definir a %SERVER_NAME% variável como nome de host totalmente qualificado do computador no qual o serviço será executado. Observe que o arquivo setup.bat deve ser executado em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

  4. Copie o certificado do servidor no repositório CurrentUser-TrustedPeople do cliente. Você não precisa fazer isso, exceto quando o certificado do servidor é emitido por um emissor confiável do cliente.

  5. No arquivo App.config no computador de serviço, altere o valor do endereço base para especificar um nome de computador totalmente qualificado em vez de localhost.

  6. No computador de serviço, inicie o service.exe a partir de um prompt de comando.

  7. Copie os arquivos do programa cliente da pasta \client\bin\, na pasta específica do idioma, para o computador cliente.

  8. No arquivo Client.exe.config no computador cliente, altere o valor do endereço do ponto de extremidade para corresponder ao novo endereço do serviço.

  9. No computador cliente, inicialize Client.exe a partir de um prompt de comando.

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

Para limpar após a amostra

  1. Execute Cleanup.bat na pasta de exemplos depois de concluir a execução do exemplo.