Como: proteger um serviço com credenciais do Windows
Este tópico mostra como habilitar a segurança de transporte em um serviço WCF que reside em um domínio do Windows e é chamado por clientes no mesmo domínio. Para obter mais informações sobre esse cenário, confira Segurança de Transporte com Autenticação do Windows. Para obter um aplicativo de exemplo, confira o exemplo WSHttpBinding.
Este tópico pressupõe que você tem uma interface de contrato existente e que a implementação já foi definida e acrescenta a isso. Você também pode modificar um serviço e um cliente existentes.
Você pode proteger um serviço com credenciais do Windows totalmente no código. Como alternativa, você pode omitir parte do código usando um arquivo de configuração. Este tópico mostra as duas maneiras. Use apenas uma das maneiras, não ambas.
Os três primeiros procedimentos mostram como proteger o serviço usando o código. O quarto e quinto procedimento mostram como fazer isso com um arquivo de configuração.
Usando o código
O código completo para o serviço e o cliente está na seção Exemplo ao final deste tópico.
O primeiro procedimento explica a criação e a configuração de uma classe WSHttpBinding no código. A associação usa o transporte HTTP. A mesma associação é usada no cliente.
Para criar um WSHttpBinding que usa credenciais do Windows e segurança de mensagens
O código desse procedimento é inserido no início do método
Run
da classeTest
no código de serviço na seção Exemplo.Criar uma instância da classe WSHttpBinding.
Defina a propriedade Mode da classe WSHttpSecurity como Message.
Defina a propriedade ClientCredentialType da classe MessageSecurityOverHttp como Windows.
O código desse procedimento é o seguinte:
// First procedure: // create a WSHttpBinding that uses Windows credentials and message security WSHttpBinding myBinding = new WSHttpBinding(); myBinding.Security.Mode = SecurityMode.Message; myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
Dim myBinding As New WSHttpBinding() myBinding.Security.Mode = SecurityMode.Message myBinding.Security.Message.ClientCredentialType = MessageCredentialType.Windows
Usando a Associação em um Serviço
Este é o segundo procedimento, que mostra como usar a associação em um serviço auto-hospedado. Para obter mais informações sobre como hospedar serviços, confira Serviços de Hospedagem.
Para usar uma associação em um serviço
Insira o código deste procedimento após o código do procedimento anterior.
Crie uma variável Type chamada
contractType
e atribua a ela o tipo da interface (ICalculator
). Ao usar o Visual Basic, use o operadorGetType
. Ao usar o C#, use a palavra-chavetypeof
.Crie uma segunda variável Type chamada
serviceType
e atribua a ela o tipo do contrato implementado (Calculator
).Crie uma instância da classe Uri chamada
baseAddress
com o endereço básico do serviço. O endereço básico deve ter um esquema correspondente ao transporte. Nesse caso, o esquema de transporte é HTTP e o endereço inclui o "localhost" especial do URI (Uniform Resource Identifier) e um número de porta (8036), bem como um endereço básico do ponto de extremidade ("serviceModelSamples/):http://localhost:8036/serviceModelSamples/
.Crie uma instância da classe ServiceHost com as variáveis
serviceType
ebaseAddress
.Adicione um ponto de extremidade ao serviço usando a
contractType
, a associação e o nome de um ponto de extremidade (secureCalculator). Um cliente deve concatenar o endereço básico e o nome do ponto de extremidade ao iniciar uma chamada ao serviço.Chame o método Open para iniciar o serviço. O código desse procedimento é mostrado aqui:
// 2nd Procedure: // Use the binding in a service // Create the Type instances for later use and the URI for // the base address. Type contractType = typeof(ICalculator); Type serviceType = typeof(Calculator); Uri baseAddress = new Uri("http://localhost:8036/SecuritySamples/"); // Create the ServiceHost and add an endpoint, then start // the service. ServiceHost myServiceHost = new ServiceHost(serviceType, baseAddress); myServiceHost.AddServiceEndpoint (contractType, myBinding, "secureCalculator"); //enable metadata ServiceMetadataBehavior smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; myServiceHost.Description.Behaviors.Add(smb); myServiceHost.Open();
' Create the Type instances for later use and the URI for ' the base address. Dim contractType As Type = GetType(ICalculator) Dim serviceType As Type = GetType(Calculator) Dim baseAddress As New Uri("http://localhost:8036/serviceModelSamples/") ' Create the ServiceHost and add an endpoint, then start ' the service. Dim myServiceHost As New ServiceHost(serviceType, baseAddress) myServiceHost.AddServiceEndpoint(contractType, myBinding, "secureCalculator") myServiceHost.Open()
Usando a Associação em um Cliente
Este procedimento mostra como gerar um proxy que se comunica com o serviço. O proxy é gerado com a Ferramenta de Utilitário de Metadados do ServiceModel (Svcutil.exe), que usa os metadados de serviço para criar o proxy.
Esse procedimento também cria uma instância da classe WSHttpBinding para se comunicar com o serviço e, em seguida, chama o serviço.
Este exemplo usa apenas o código para criar o cliente. Como alternativa, você pode usar um arquivo de configuração, que é mostrado na seção após este procedimento.
Para usar uma associação em um cliente com o código
Use a ferramenta SvcUtil.exe para gerar o código de proxy nos metadados do serviço. Para obter mais informações, confira Instruções: criar um cliente. O código de proxy gerado herda da classe ClientBase<TChannel>, que garante que cada cliente tenha as propriedades, os métodos e os construtores necessários para se comunicar com um serviço WCF. Neste exemplo, o código gerado inclui a classe
CalculatorClient
, que implementa a interfaceICalculator
, habilitando a compatibilidade com o código de serviço.O código desse procedimento é inserido no início do método
Main
do programa do cliente.Crie uma instância da classe WSHttpBinding e defina o modo de segurança como
Message
e o tipo de credencial do cliente comoWindows
. O exemplo chama a variável declientBinding
.Crie uma instância da classe EndpointAddress chamada
serviceAddress
. Inicialize a instância com o endereço básico concatenado com o nome do ponto de extremidade.Crie uma instância da classe do cliente gerada com as variáveis
serviceAddress
eclientBinding
.Chame o método Open, conforme mostrado no exemplo a seguir.
Chame o serviço e mostre os resultados.
// 3rd Procedure: // Creating a binding and using it in a service // To run using config, comment the following lines, and uncomment out the code // following this section WSHttpBinding b = new WSHttpBinding(SecurityMode.Message); b.Security.Message.ClientCredentialType = MessageCredentialType.Windows; EndpointAddress ea = new EndpointAddress("Http://localhost:8036/SecuritySamples/secureCalculator"); CalculatorClient cc = new CalculatorClient(b, ea); cc.Open(); // Now call the service and display the results // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = cc.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result); // Closing the client gracefully closes the connection and cleans up resources. cc.Close();
Dim b As New WSHttpBinding(SecurityMode.Message) b.Security.Message.ClientCredentialType = MessageCredentialType.Windows Dim ea As New EndpointAddress("net.tcp://machinename:8036/endpoint") Dim cc As New CalculatorClient(b, ea) cc.Open() ' Alternatively, use a binding name from a configuration file generated by the ' SvcUtil.exe tool to create the client. Omit the binding and endpoint address ' because that information is provided by the configuration file. ' CalculatorClass cc = new CalculatorClient("ICalculator_Binding")
Usando o Arquivo de Configuração
Em vez de criar a associação com o código do procedimento, você pode usar o código a seguir mostrado para a seção de associações do arquivo de configuração.
Se você ainda não tiver um serviço definido, confira Design e implementação de serviços e Configuração de serviços.
Observação
Esse código de configuração é usado nos arquivos de configuração do serviço e do cliente.
Para habilitar a segurança de transferência em um serviço em um domínio do Windows usando a configuração
Adicione um elemento <wsHttpBinding> à seção do elemento de <associação> do arquivo de configuração.
Adicione um elemento
<binding>
ao elemento<WSHttpBinding>
e defina o atributoconfigurationName
como o valor adequado para o aplicativo.Adicione um elemento de
<security>
e defina o atributomode
como Mensagem.Adicione um elemento de
<message>
e defina o atributoclientCredentialType
como Windows.No arquivo de configuração do serviço, substitua a seção
<bindings>
pelo código a seguir. Se você ainda não tiver um arquivo de configuração de serviço, confira Uso de associações para configurar serviços e clientes.<bindings> <wsHttpBinding> <binding name = "wsHttpBinding_Calculator"> <security mode="Message"> <message clientCredentialType="Windows"/> </security> </binding> </wsHttpBinding> </bindings>
Usando a Associação em um Cliente
Este procedimento mostra como gerar dois arquivos: um proxy que se comunica com o serviço e um arquivo de configuração. Ele também descreve as alterações no programa do cliente, que é o terceiro arquivo usado no cliente.
Para usar uma associação em um cliente com a configuração
Use a ferramenta SvcUtil.exe para gerar o código de proxy e o arquivo de configuração nos metadados do serviço. Para obter mais informações, confira Instruções: criar um cliente.
Substitua a seção de <associações> do arquivo de configuração gerado pelo código de configuração da seção anterior.
O código do procedimento é inserido no início do método
Main
do programa do cliente.Crie uma instância da classe gerada do cliente, passando o nome da associação no arquivo de configuração como parâmetro de entrada.
Chame o método Open, conforme mostrado no exemplo a seguir.
Chame o serviço e mostre os resultados.
// 4th Procedure: // Using config instead of the binding-related code // In this case, use a binding name from a configuration file generated by the // SvcUtil.exe tool to create the client. Omit the binding and endpoint address // because that information is provided by the configuration file. CalculatorClient cc = new CalculatorClient("ICalculator_Binding"); cc.Open(); // Now call the service and display the results // Call the Add service operation. double value1 = 100.00D; double value2 = 15.99D; double result = cc.Add(value1, value2); Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result); // Closing the client gracefully closes the connection and cleans up resources. cc.Close();
Exemplo
using System;
using System.Collections;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace Microsoft.Security.Samples
{
public class Test
{
static void Main()
{
Test t = new Test();
Console.WriteLine("Starting....");
t.Run();
}
private void Run()
{
// First procedure:
// create a WSHttpBinding that uses Windows credentials and message security
WSHttpBinding myBinding = new WSHttpBinding();
myBinding.Security.Mode = SecurityMode.Message;
myBinding.Security.Message.ClientCredentialType =
MessageCredentialType.Windows;
// 2nd Procedure:
// Use the binding in a service
// Create the Type instances for later use and the URI for
// the base address.
Type contractType = typeof(ICalculator);
Type serviceType = typeof(Calculator);
Uri baseAddress = new
Uri("http://localhost:8036/SecuritySamples/");
// Create the ServiceHost and add an endpoint, then start
// the service.
ServiceHost myServiceHost =
new ServiceHost(serviceType, baseAddress);
myServiceHost.AddServiceEndpoint
(contractType, myBinding, "secureCalculator");
//enable metadata
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
myServiceHost.Description.Behaviors.Add(smb);
myServiceHost.Open();
Console.WriteLine("Listening");
Console.WriteLine("Press Enter to close the service");
Console.ReadLine();
myServiceHost.Close();
}
}
[ServiceContract]
public interface ICalculator
{
[OperationContract]
double Add(double a, double b);
}
public class Calculator : ICalculator
{
public double Add(double a, double b)
{
return a + b;
}
}
}
using System;
using System.Collections.Generic;
using System.ServiceModel;
namespace Client
{
static class SecureClientCode
{
static void Main()
{
// 3rd Procedure:
// Creating a binding and using it in a service
// To run using config, comment the following lines, and uncomment out the code
// following this section
WSHttpBinding b = new WSHttpBinding(SecurityMode.Message);
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
EndpointAddress ea = new EndpointAddress("Http://localhost:8036/SecuritySamples/secureCalculator");
CalculatorClient cc = new CalculatorClient(b, ea);
cc.Open();
// Now call the service and display the results
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = cc.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Closing the client gracefully closes the connection and cleans up resources.
cc.Close();
}
static void Main2()
{
// 4th Procedure:
// Using config instead of the binding-related code
// In this case, use a binding name from a configuration file generated by the
// SvcUtil.exe tool to create the client. Omit the binding and endpoint address
// because that information is provided by the configuration file.
CalculatorClient cc = new CalculatorClient("ICalculator_Binding");
cc.Open();
// Now call the service and display the results
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = cc.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
// Closing the client gracefully closes the connection and cleans up resources.
cc.Close();
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft.ServiceModel.Samples", ConfigurationName = "ICalculator")]
public interface ICalculator
{
[System.ServiceModel.OperationContractAttribute(Action = "http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction = "http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")]
double Add(double n1, double n2);
[System.ServiceModel.OperationContractAttribute(Action = "http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction = "http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")]
double Subtract(double n1, double n2);
[System.ServiceModel.OperationContractAttribute(Action = "http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction = "http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")]
double Multiply(double n1, double n2);
[System.ServiceModel.OperationContractAttribute(Action = "http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction = "http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")]
double Divide(double n1, double n2);
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public interface ICalculatorChannel : ICalculator, System.ServiceModel.IClientChannel
{
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public class CalculatorClient : System.ServiceModel.ClientBase<ICalculator>, ICalculator
{
public CalculatorClient()
{
}
public CalculatorClient(string endpointConfigurationName)
:
base(endpointConfigurationName)
{
}
public CalculatorClient(string endpointConfigurationName, string remoteAddress)
:
base(endpointConfigurationName, remoteAddress)
{
}
public CalculatorClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress)
:
base(endpointConfigurationName, remoteAddress)
{
}
public CalculatorClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
:
base(binding, remoteAddress)
{
}
public double Add(double n1, double n2)
{
return base.Channel.Add(n1, n2);
}
public double Subtract(double n1, double n2)
{
return base.Channel.Subtract(n1, n2);
}
public double Multiply(double n1, double n2)
{
return base.Channel.Multiply(n1, n2);
}
public double Divide(double n1, double n2)
{
return base.Channel.Divide(n1, n2);
}
}
}
Imports System.Collections.Generic
Imports System.ServiceModel
Public Class Program
Shared Sub Main()
Dim b As New WSHttpBinding(SecurityMode.Message)
b.Security.Message.ClientCredentialType = MessageCredentialType.Windows
Dim ea As New EndpointAddress("net.tcp://machinename:8036/endpoint")
Dim cc As New CalculatorClient(b, ea)
cc.Open()
' Alternatively, use a binding name from a configuration file generated by the
' SvcUtil.exe tool to create the client. Omit the binding and endpoint address
' because that information is provided by the configuration file.
' CalculatorClass cc = new CalculatorClient("ICalculator_Binding")
End Sub
End Class
<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0"), System.ServiceModel.ServiceContractAttribute([Namespace]:="http://Microsoft.ServiceModel.Samples", ConfigurationName:="ICalculator")> _
Public Interface ICalculator
<System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")> _
Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
<System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")> _
Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
<System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")> _
Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
<System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")> _
Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
End Interface 'ICalculator
<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _
Public Interface ICalculatorChannel
: Inherits ICalculator, System.ServiceModel.IClientChannel
End Interface 'ICalculatorChannel
<System.Diagnostics.DebuggerStepThroughAttribute(), System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _
Public Class CalculatorClient
Inherits System.ServiceModel.ClientBase(Of ICalculator)
Implements ICalculator
Public Sub New()
'
End Sub
Public Sub New(ByVal endpointConfigurationName As String)
MyBase.New(endpointConfigurationName)
End Sub
Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As String)
MyBase.New(endpointConfigurationName, remoteAddress)
End Sub
Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
MyBase.New(endpointConfigurationName, remoteAddress)
End Sub
Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
MyBase.New(binding, remoteAddress)
End Sub
Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
Return MyBase.Channel.Add(n1, n2)
End Function 'Add
Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
Return MyBase.Channel.Subtract(n1, n2)
End Function 'Subtract
Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
Return MyBase.Channel.Multiply(n1, n2)
End Function 'Multiply
Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
Return MyBase.Channel.Divide(n1, n2)
End Function 'Divide
End Class