Επεξεργασία

Κοινή χρήση μέσω


How to: Secure a Service with Windows Credentials

This topic shows how to enable transport security on a Windows Communication Foundation (WCF) service that resides in a Windows domain and is called by clients in the same domain. For more information about this scenario, see Transport Security with Windows Authentication. For a sample application, see the WSHttpBinding sample.

This topic assumes you have an existing contract interface and implementation already defined, and adds on to that. You can also modify an existing service and client.

You can secure a service with Windows credentials completely in code. Alternatively, you can omit some of the code by using a configuration file. This topic shows both ways. Be sure you only use one of the ways, not both.

The first three procedures show how to secure the service using code. The fourth and fifth procedure shows how to do it with a configuration file.

Using Code

The complete code for the service and the client is in the Example section at the end of this topic.

The first procedure walks through creating and configuring a WSHttpBinding class in code. The binding uses the HTTP transport. The same binding is used on the client.

To create a WSHttpBinding that uses Windows credentials and message security

  1. This procedure's code is inserted at the beginning of the Run method of the Test class in the service code in the Example section.

  2. Create an instance of the WSHttpBinding class.

  3. Set the Mode property of the WSHttpSecurity class to Message.

  4. Set the ClientCredentialType property of the MessageSecurityOverHttp class to Windows.

  5. The code for this procedure is as follows:

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

Using the Binding in a Service

This is the second procedure, which shows how to use the binding in a self-hosted service. For more information about hosting services see Hosting Services.

To use a binding in a service
  1. Insert this procedure's code after the code from the preceding procedure.

  2. Create a Type variable named contractType and assign it the type of the interface (ICalculator). When using Visual Basic, use the GetType operator; when using C#, use the typeof keyword.

  3. Create a second Type variable named serviceType and assign it the type of the implemented contract (Calculator).

  4. Create an instance of the Uri class named baseAddress with the base address of the service. The base address must have a scheme that matches the transport. In this case, the transport scheme is HTTP, and the address includes the special Uniform Resource Identifier (URI) "localhost" and a port number (8036) as well as a base endpoint address ("serviceModelSamples/): http://localhost:8036/serviceModelSamples/.

  5. Create an instance of the ServiceHost class with the serviceType and baseAddress variables.

  6. Add an endpoint to the service using the contractType, binding, and an endpoint name (secureCalculator). A client must concatenate the base address and the endpoint name when initiating a call to the service.

  7. Call the Open method to start the service. The code for this procedure is shown here:

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

Using the Binding in a Client

This procedure shows how to generate a proxy that communicates with the service. The proxy is generated with the ServiceModel Metadata Utility Tool (Svcutil.exe) which uses the service metadata to create the proxy.

This procedure also creates an instance of the WSHttpBinding class to communicate with the service, and then calls the service.

This example uses only code to create the client. As an alternative, you can use a configuration file, which is shown in the section following this procedure.

To use a binding in a client with code

  1. Use the SvcUtil.exe tool to generate the proxy code from the service's metadata. For more information, see How to: Create a Client. The generated proxy code inherits from the ClientBase<TChannel> class, which ensures that every client has the necessary constructors, methods, and properties to communicate with a WCF service. In this example, the generated code includes the CalculatorClient class, which implements the ICalculator interface, enabling compatibility with the service code.

  2. This procedure's code is inserted at the beginning of the Main method of the client program.

  3. Create an instance of the WSHttpBinding class and set its security mode to Message and its client credential type to Windows. The example names the variable clientBinding.

  4. Create an instance of the EndpointAddress class named serviceAddress. Initialize the instance with the base address concatenated with the endpoint name.

  5. Create an instance of the generated client class with the serviceAddress and the clientBinding variables.

  6. Call the Open method, as shown in the following code.

  7. Call the service and display the results.

    // 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")
    

Using the Configuration File

Instead of creating the binding with procedural code, you can use the following code shown for the bindings section of the configuration file.

If you do not already have a service defined, see Designing and Implementing Services, and Configuring Services.

Note

This configuration code is used in both the service and client configuration files.

To enable transfer security on a service in a Windows domain using configuration

  1. Add a <wsHttpBinding> element to the <bindings> element section of the configuration file.

  2. Add a <binding> element to the <WSHttpBinding> element and set the configurationName attribute to a value appropriate to your application.

  3. Add a <security> element and set the mode attribute to Message.

  4. Add a <message> element and set the clientCredentialType attribute to Windows.

  5. In the service's configuration file, replace the <bindings> section with the following code. If you do not already have a service configuration file, see Using Bindings to Configure Services and Clients.

    <bindings>
      <wsHttpBinding>
       <binding name = "wsHttpBinding_Calculator">
         <security mode="Message">
           <message clientCredentialType="Windows"/>
         </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    

Using the Binding in a Client

This procedure shows how to generate two files: a proxy that communicates with the service and a configuration file. It also describes changes to the client program, which is the third file used on the client.

To use a binding in a client with configuration

  1. Use the SvcUtil.exe tool to generate the proxy code and configuration file from the service's metadata. For more information, see How to: Create a Client.

  2. Replace the <bindings> section of the generated configuration file with the configuration code from the preceding section.

  3. Procedural code is inserted at the beginning of the Main method of the client program.

  4. Create an instance of the generated client class passing the name of the binding in the configuration file as an input parameter.

  5. Call the Open method, as shown in the following code.

  6. Call the service and display the results.

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

Example

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

See also