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 personalizado são úteis em uma variedade de casos, como:
Quando quiser substituir o mecanismo de autenticação padrão associado a um token.
Quando estiver criando um token personalizado.
Esse exemplo demonstra os seguintes itens:
Como um cliente pode 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 conecta com o autenticador de token personalizado.
O servidor pode ser autenticado usando o certificado X.509 do servidor.
Este exemplo também mostra como a identidade do chamador está acessível no WCF após o processo de autenticação de token personalizado.
O serviço expõe um único ponto de extremidade para se comunicar com o serviço, definido usando o arquivo de configuração App.config. O ponto de extremidade é composto por um endereço, uma associação e um contrato. A associação é configurada com um wsHttpBinding
padrão, com o modo de segurança definido como mensagem – o modo padrão do wsHttpBinding
. Este exemplo define o wsHttpBinding
padrão para usar a autenticação do nome de usuário. O serviço também configura o certificado de serviço usando o comportamento serviceCredentials
. O comportamento securityCredentials
permite especificar 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 etapas a seguir para criar um autenticador de token personalizado:
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(); }
Fornecer uma política de autorização retornada pelo autenticador de token personalizado.
Este exemplo fornece sua própria implementação de IAuthorizationPolicy chamadas de
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; } }
Gravar um gerenciador de token de segurança personalizado.
O SecurityTokenManager é usado para criar um SecurityTokenAuthenticator para objetos SecurityTokenRequirement específicos que são passados para ele no método
CreateSecurityTokenAuthenticator
. O gerenciador de token 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 da classe ServiceCredentialsSecurityTokenManager e substitui o métodoCreateSecurityTokenAuthenticator
para retornar o autenticador de token de nome de usuário personalizado quando os requisitos de token passados indicam que o 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); } } }
Gravar 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); } }
Configurar o serviço para usar a credencial de serviço personalizada.
Para que o serviço use a credencial de serviço personalizada, excluímos 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 a amostra, as solicitações de operação e as respostas 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 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. Esse arquivo em lote precisa ser modificado para funcionar em computadores ou em um caso não hospedado.
A seguir, oferecemos uma visão geral das diferentes seções dos arquivos em lote que podem ser modificadas 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 padrão neste arquivo de 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
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 pelo 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
Observação
O arquivo de lote de instalação foi projetado para ser executado em um Prompt de Comando do SDK do Windows. Ele requer que a variável de ambiente MSSDK aponte para o diretório onde o SDK está instalado. Essa variável de ambiente é definida automaticamente em um Prompt de Comando do SDK do Windows.
Para configurar e compilar a amostra
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para compilar a solução, siga as instruções contidas em Como compilar as amostras do Windows Communication Foundation.
Para executar a amostra no mesmo computador
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 instalará todos os certificados necessários para executar o exemplo.
Observação
O arquivo de 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.
Inicialize o service.exe a partir do service\bin.
Inicialize o client.exe a partir do \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
Crie um diretório no computador de serviço para os binários de serviço.
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.
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 a Setup.bat se definir a variável
%SERVER_NAME%
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.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.
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.
No computador de serviço, inicie o service.exe a partir de um prompt de comando.
Copie os arquivos do programa cliente da pasta \client\bin\ na pasta específica do idioma, para o computador cliente.
No arquivo Client.exe.config do computador cliente, altere o valor do endereço do ponto de extremidade para que ele corresponda ao novo endereço do serviço.
No computador cliente, inicie Client.exe a partir de um prompt de comando.
Se o cliente e o serviço não puderem se comunicar, confira Dicas de solução de problemas para exemplos de WCF.
Para fazer uma limpeza após o exemplo
- Execute Cleanup.bat na pasta de amostras depois de concluir a execução da amostra.