作法:使用 Windows 認證來確保服務安全
本主題說明如何針對位於 Windows 網域中,而且是由相同網域中的用戶端所呼叫的 Windows Communication Foundation (WCF) 服務,啟用其上面的傳輸安全性。 如需此情節的詳細資訊,請參閱 Windows 驗證的傳輸安全性。 如需範例應用程式,請參閱 WSHttpBinding 範例。
這個主題假設您擁有現有的合約介面、已定義了實作 (Implementation),以及一些附加內容。 您也可以修改現有的服務和用戶端。
您可以完全使用程式碼,透過 Windows 認證來確保服務安全。 或者,您也可以使用組態檔來省略部分程式碼。 這個主題會說明這兩種方式。 請務必只使用其中一種方式,不要兩種都使用。
前三個程序會說明如何使用程式碼來保護服務安全。 第四和第五個程序會說明如何使用組態檔來執行這項作業。
使用程式碼
服務和用戶端的完整程式碼已提供於本主題結尾的「範例」一節。
第一個程序會逐步解說如何使用程式碼建立和設定 WSHttpBinding 類別。 繫結會使用 HTTP 傳輸。 用戶端上會使用相同的繫結。
建立會使用 Windows 認證和訊息安全性的 WSHttpBinding
這個程序的程式碼會插入至「範例」一節服務程式碼中
Run
類別的Test
方法開頭。建立 WSHttpBinding 類別的執行個體。
將 Mode 類別的 WSHttpSecurity 屬性設定為 Message。
將 ClientCredentialType 類別的 MessageSecurityOverHttp 屬性設定為 Windows。
這項程序的程式碼如下所示:
// 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
在服務中使用繫結
這是第二個程序,說明如何在自我裝載的服務中使用繫結。 如需裝載服務的詳細資訊,請參閱裝載服務。
在服務中使用繫結
將這個程序的程式碼插入到前一個程序的程式碼後面。
建立名稱為 Type 的
contractType
變數,並將其類型指派為介面 (ICalculator
)。 使用 Visual Basic 時,請使用GetType
運算子;使用 C# 時,請使用typeof
關鍵字。建立第二個名稱為 Type 的
serviceType
變數,並將其類型指派為實作的合約 (Calculator
)。使用服務的基底位址,建立名稱為 Uri 之
baseAddress
類別的執行個體。 基底位址必須具有符合傳輸的配置。 在此情況下,傳輸配置為 HTTP,而位址中則包含特殊統一資源識別項 (URI) "localhost"、連接埠號碼 (8036) 和基底端點位址 (serviceModelSamples/):http://localhost:8036/serviceModelSamples/
。使用 ServiceHost 和
serviceType
變數,建立baseAddress
類別的執行個體。使用
contractType
、繫結和端點名稱 (secureCalculator),將端點新增至服務。 初始化服務的呼叫時,用戶端必須串連基底位址和端點名稱。呼叫 Open 方法啟動服務。 這項程序的程式碼如下所示:
// 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()
在用戶端中使用繫結
這項程序會說明如何產生與服務進行通訊的 Proxy。 Proxy 是透過 ServiceModel 中繼資料公用程式工具 (Svcutil.exe)所產生,該工具使用服務中繼資料來建立 Proxy。
此程序還會建立可與服務進行通訊之 WSHttpBinding 類別的執行個體,然後呼叫該服務。
這個範例只會使用程式碼來建立用戶端。 或者,您可以使用組態檔,該檔案會在此程序之後的章節中說明。
搭配程式碼在用戶端中使用繫結
使用 SvcUtil.exe 工具,從服務的中繼資料產生 Proxy 程式碼。 如需詳細資訊,請參閱操作說明:建立用戶端。 產生的 Proxy 程式碼繼承自 ClientBase<TChannel> 類別,這樣可確保每個用戶端都有與 WCF 服務進行通訊時所需要的建構函式、方法和屬性。 在這個範例中,產生的程式碼包含
CalculatorClient
類別,這個類別會實作ICalculator
介面,因此就會與服務程式碼相容。此程序的程式碼會插入至用戶端程式的
Main
方法開頭。建立 WSHttpBinding 類別的執行個體,將其安全性模式設定為
Message
,並將其用戶端認證類型設定為Windows
。 這個範例會將變數命名為clientBinding
。建立名稱為 EndpointAddress 之
serviceAddress
類別的執行個體。 使用與端點名稱串連的基底位址初始化執行個體。使用
serviceAddress
和clientBinding
變數,建立產生之用戶端類別的執行個體。呼叫 Open 方法,如下列程式碼所示。
呼叫服務並顯示結果。
// 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")
使用組態檔
如果不使用程序程式碼建立繫結,您可以改用下面針對組態檔之繫結區段所顯示的程式碼。
注意
這個組態程式碼會同時在服務和用戶端組態檔中使用。
使用組態在 Windows 網域的服務上啟用傳輸安全性
將 <wsHttpBinding> 元素新增至組態檔的 <bindings> 元素區段。
將
<binding>
元素新增至<WSHttpBinding>
元素,並將configurationName
屬性設定為適用於您應用程式的值。新增
<security>
元素,並將mode
屬性設定為 Message。新增
<message>
元素,並將clientCredentialType
屬性設定為 Windows。在服務的組態檔中,將
<bindings>
區段取代為下列程式碼。 如果您還沒有服務組態檔,請參閱使用繫結來設定服務和用戶端。<bindings> <wsHttpBinding> <binding name = "wsHttpBinding_Calculator"> <security mode="Message"> <message clientCredentialType="Windows"/> </security> </binding> </wsHttpBinding> </bindings>
在用戶端中使用繫結
此程序會說明如何產生兩個檔案:與服務進行通訊的 Proxy 及組態檔。 此外還會說明用戶端程式的變更,也就是用戶端上使用的第三個檔案。
搭配組態在用戶端中使用繫結
使用 SvcUtil.exe 工具,從服務的中繼資料產生 Proxy 程式碼和組態檔。 如需詳細資訊,請參閱操作說明:建立用戶端。
將已產生之組態檔的 <bindings> 區段取代為前一個區段的組態程式碼。
程序程式碼會插入至用戶端程式的
Main
方法開頭。建立已產生之用戶端類別的執行個體,將組態檔中繫結的名稱當做輸入參數傳遞。
呼叫 Open 方法,如下列程式碼所示。
呼叫服務並顯示結果。
// 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();
範例
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