HOW TO:使用 WCF 用戶端來存取 WSE 3.0 服務
當 WCF 用戶端設定為使用 WS-Addressing August 2004 版本規格時,Windows Communication Foundation (WCF) 用戶端的連線層級與 Microsoft .NET 服務的 Web Services Enhancements (WSE) 3.0 相容。 不過,WSE 3.0 服務不支援中繼資料交換 (MEX) 通訊協定,當您使用ServiceModel 中繼資料公用程式工具 (Svcutil.exe) 來建立 WCF 用戶端類別時,安全性設定不會套用至所產生的 WCF 用戶端。 因此,在產生 WCF 用戶端之後,您必須指定 WSE 3.0 服務所需的安全性設定。
您可以使用自訂繫結來套用這些安全性設定,將 WSE 3.0 服務的需求,以及 WSE 3.0 服務和 WCF 用戶端之間的互通需求納入考量。 這些互通性需求包含上述的 WS-Addressing August 2004 規格使用和 WSE 3.0 預設訊息保護 SignBeforeEncrypt。 WCF 的預設訊息保護為 SignBeforeEncryptAndEncryptSignature。 本主題會詳細說明如何建立可與 WSE 3.0 服務交互操作的 WCF 繫結。此外,WCF 也提供加入此繫結的範例。如需詳細資訊如需這個範例的詳細資訊,請參閱Interoperating with WSE。
若要使用 WCF 用戶端來存取 WSE 3.0 Web 服務
執行ServiceModel 中繼資料公用程式工具 (Svcutil.exe),以建立 WSE 3.0 Web 服務的 WCF 用戶端。
隨即建立 WSE 3.0 Web 服務的 WCF 用戶端。 因為 WSE 3.0 不支援 MEX 通訊協定,所以您無法使用此工具擷取 Web 服務的安全性需求。 應用程式開發人員必須為用戶端加入安全性設定。
如需詳細資訊如需建立 WCF 用戶端的詳細資訊,請參閱 HOW TO:建立 Windows Communication Foundation 用戶端。
建立類別,表示可與 WSE 3.0 Web 服務通訊的繫結。
下列類別是Interoperating with WSE範例的一部分:
建立從 Binding 類別衍生的類別。
下列程式碼範例會建立一個名為
WseHttpBinding
的類別,此類別衍生自 Binding 類別。Public Class WseHttpBinding Inherits Binding
public class WseHttpBinding : Binding {
將屬性加入至類別,這些屬性會指定 WSE 服務所使用的 WSE 通行判斷提示 (Turnkey Assertion)、是否需要衍生金鑰、是否使用安全工作階段、是否需要簽章確認,以及訊息保護設定。 在 WSE 3.0 中,通行判斷提示會指定用戶端或 Web 服務的安全性需求,這與 WCF 中的繫結驗證模式類似。
下列程式碼範例會定義
SecurityAssertion
、RequireDerivedKeys
、EstablishSecurityContext
和MessageProtectionOrder
屬性,這些屬性會分別指定 WSE 通行判斷提示、是否需要衍生金鑰、是否使用安全工作階段、是否需要簽章確認,以及訊息保護設定。Public Property SecurityAssertion() As WseSecurityAssertion Get Return assertion End Get Set(ByVal value As WseSecurityAssertion) assertion = value End Set End Property Private m_requireDerivedKeys As Boolean Public Property RequireDerivedKeys() As Boolean Get Return m_requireDerivedKeys End Get Set(ByVal value As Boolean) m_requireDerivedKeys = value End Set End Property Private m_establishSecurityContext As Boolean Public Property EstablishSecurityContext() As Boolean Get Return m_establishSecurityContext End Get Set(ByVal value As Boolean) m_establishSecurityContext = value End Set End Property Private m_requireSignatureConfirmation As Boolean Public Property RequireSignatureConfirmation() As Boolean Get Return m_requireSignatureConfirmation End Get Set(ByVal value As Boolean) m_requireSignatureConfirmation = value End Set End Property Private m_messageProtectionOrder As MessageProtectionOrder Public Property MessageProtectionOrder() As MessageProtectionOrder Get Return m_messageProtectionOrder End Get Set(ByVal value As MessageProtectionOrder) m_messageProtectionOrder = value End Set End Property
private WseSecurityAssertion assertion; public WseSecurityAssertion SecurityAssertion { get { return assertion; } set { assertion = value; } } private bool requireDerivedKeys; public bool RequireDerivedKeys { get { return requireDerivedKeys; } set { requireDerivedKeys = value; } } private bool establishSecurityContext; public bool EstablishSecurityContext { get { return establishSecurityContext; } set { establishSecurityContext = value; } } private bool requireSignatureConfirmation; public bool RequireSignatureConfirmation { get { return requireSignatureConfirmation; } set { requireSignatureConfirmation = value; } } private MessageProtectionOrder messageProtectionOrder; public MessageProtectionOrder MessageProtectionOrder { get { return messageProtectionOrder; } set { messageProtectionOrder = value; } }
覆寫 CreateBindingElements 方法來設定繫結屬性。
下列程式碼範例會藉由取得
SecurityAssertion
和MessageProtectionOrder
屬性的值,指定傳輸、訊息編碼和訊息保護設定。Public Overloads Overrides Function CreateBindingElements() As BindingElementCollection 'SecurityBindingElement sbe = bec.Find<SecurityBindingElement>(); Dim bec As New BindingElementCollection() ' By default http transport is used Dim securityBinding As SecurityBindingElement Dim transport As BindingElement Select Case assertion Case WseSecurityAssertion.UsernameOverTransport transport = New HttpsTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateUserNameOverTransportBindingElement(), TransportSecurityBindingElement) If m_establishSecurityContext = True Then Throw New InvalidOperationException("Secure Conversation is not supported for this Security Assertion Type") End If If m_requireSignatureConfirmation = True Then Throw New InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type") End If Exit Select Case WseSecurityAssertion.MutualCertificate10 transport = New HttpTransportBindingElement() securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10) If m_requireSignatureConfirmation = True Then Throw New InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type") End If DirectCast(securityBinding, AsymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.UsernameForCertificate transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateUserNameForCertificateBindingElement(), SymmetricSecurityBindingElement) ' We want signatureconfirmation on the bootstrap process ' either for the application messages or for the RST/RSTR DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.AnonymousForCertificate transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateAnonymousForCertificateBindingElement(), SymmetricSecurityBindingElement) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.MutualCertificate11 transport = New HttpTransportBindingElement() securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case WseSecurityAssertion.Kerberos transport = New HttpTransportBindingElement() securityBinding = DirectCast(SecurityBindingElement.CreateKerberosBindingElement(), SymmetricSecurityBindingElement) DirectCast(securityBinding, SymmetricSecurityBindingElement).RequireSignatureConfirmation = m_requireSignatureConfirmation DirectCast(securityBinding, SymmetricSecurityBindingElement).MessageProtectionOrder = m_messageProtectionOrder Exit Select Case Else Throw New NotSupportedException("This supplied Wse security assertion is not supported") End Select 'Set defaults for the security binding securityBinding.IncludeTimestamp = True ' Derived Keys ' Set the preference for derived keys before creating the binding for SecureConversation. securityBinding.SetKeyDerivation(m_requireDerivedKeys) 'Secure Conversation If m_establishSecurityContext = True Then Dim secureconversation As SymmetricSecurityBindingElement = DirectCast(SymmetricSecurityBindingElement.CreateSecureConversationBindingElement(securityBinding, False), SymmetricSecurityBindingElement) ' This is the default 'secureconversation.DefaultProtectionLevel = ProtectionLevel.EncryptAndSign; 'Set defaults for the secure conversation binding secureconversation.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256 ' We do not want signature confirmation on the application level messages ' when secure conversation is enabled. secureconversation.RequireSignatureConfirmation = False secureconversation.MessageProtectionOrder = m_messageProtectionOrder secureconversation.SetKeyDerivation(m_requireDerivedKeys) securityBinding = secureconversation End If ' Add the security binding to the binding collection bec.Add(securityBinding) ' Add the message encoder. Dim textelement As New TextMessageEncodingBindingElement() textelement.MessageVersion = System.ServiceModel.Channels.MessageVersion.Soap11WSAddressingAugust2004 'These are the defaults required for WSE 'textelement.MessageVersion = MessageVersion.Soap11Addressing1; 'textelement.WriteEncoding = System.Text.Encoding.UTF8; bec.Add(textelement) ' Add the transport bec.Add(transport) ' return the binding elements Return bec End Function
public override BindingElementCollection CreateBindingElements() { //SecurityBindingElement sbe = bec.Find<SecurityBindingElement>(); BindingElementCollection bec = new BindingElementCollection(); // By default http transport is used SecurityBindingElement securityBinding; BindingElement transport; switch (assertion) { case WseSecurityAssertion.UsernameOverTransport: transport = new HttpsTransportBindingElement(); securityBinding = (TransportSecurityBindingElement)SecurityBindingElement.CreateUserNameOverTransportBindingElement(); if (establishSecurityContext == true) throw new InvalidOperationException("Secure Conversation is not supported for this Security Assertion Type"); if (requireSignatureConfirmation == true) throw new InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type"); break; case WseSecurityAssertion.MutualCertificate10: transport = new HttpTransportBindingElement(); securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10); if (requireSignatureConfirmation == true) throw new InvalidOperationException("Signature Confirmation is not supported for this Security Assertion Type"); ((AsymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.UsernameForCertificate: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateUserNameForCertificateBindingElement(); // We want signatureconfirmation on the bootstrap process // either for the application messages or for the RST/RSTR ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.AnonymousForCertificate: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateAnonymousForCertificateBindingElement(); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.MutualCertificate11: transport = new HttpTransportBindingElement(); securityBinding = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; case WseSecurityAssertion.Kerberos: transport = new HttpTransportBindingElement(); securityBinding = (SymmetricSecurityBindingElement)SecurityBindingElement.CreateKerberosBindingElement(); ((SymmetricSecurityBindingElement)securityBinding).RequireSignatureConfirmation = requireSignatureConfirmation; ((SymmetricSecurityBindingElement)securityBinding).MessageProtectionOrder = messageProtectionOrder; break; default: throw new NotSupportedException("This supplied Wse security assertion is not supported"); } //Set defaults for the security binding securityBinding.IncludeTimestamp = true; // Derived Keys // set the preference for derived keys before creating SecureConversationBindingElement securityBinding.SetKeyDerivation(requireDerivedKeys); //Secure Conversation if (establishSecurityContext == true) { SymmetricSecurityBindingElement secureconversation = (SymmetricSecurityBindingElement)SymmetricSecurityBindingElement.CreateSecureConversationBindingElement( securityBinding, false); // This is the default //secureconversation.DefaultProtectionLevel = ProtectionLevel.EncryptAndSign; //Set defaults for the secure conversation binding secureconversation.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256; // We do not want signature confirmation on the application level messages // when secure conversation is enabled. secureconversation.RequireSignatureConfirmation = false; secureconversation.MessageProtectionOrder = messageProtectionOrder; secureconversation.SetKeyDerivation(requireDerivedKeys); securityBinding = secureconversation; } // Add the security binding to the binding collection bec.Add(securityBinding); // Add the message encoder. TextMessageEncodingBindingElement textelement = new TextMessageEncodingBindingElement(); textelement.MessageVersion = MessageVersion.Soap11WSAddressingAugust2004; //These are the defaults required for WSE //textelement.MessageVersion = MessageVersion.Soap11Addressing1; //textelement.WriteEncoding = System.Text.Encoding.UTF8; bec.Add(textelement); // Add the transport bec.Add(transport); // return the binding elements return bec; }
在用戶端應用程式程式碼中,加入程式碼以設定繫結屬性。
下列程式碼範例會指定 WCF 用戶端必須依照 WSE 3.0 AnonymousForCertificate 通行安全性判斷提示所定義,使用訊息保護和驗證。 此外,也需要安全工作階段和衍生金鑰。
Private Shared Sub CallWseService(ByVal usePolicyFile As Boolean) Dim address As New EndpointAddress(New Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer")) Dim binding As New WseHttpBinding() If Not usePolicyFile Then binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate binding.EstablishSecurityContext = True binding.RequireDerivedKeys = True binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt Else binding.LoadPolicy("..\wse3policyCache.config", "ServerPolicy") End If Dim client As New WSSecurityAnonymousServiceSoapClient(binding, address)
static void CallWseService(bool usePolicyFile) { EndpointAddress address = new EndpointAddress(new Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer")); WseHttpBinding binding = new WseHttpBinding(); if (!usePolicyFile) { binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate; binding.EstablishSecurityContext = true; binding.RequireDerivedKeys = true; binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt; } else binding.LoadPolicy("..\\wse3policyCache.config", "ServerPolicy"); WSSecurityAnonymousServiceSoapClient client = new WSSecurityAnonymousServiceSoapClient(binding, address);
範例
下列程式碼範例會定義自訂的繫結,此繫結會公開 WSE 3.0 通行安全性判斷提示屬性的對應屬性。 此自訂繫結 (名為 WseHttpBinding
) 接著會用來指定可與 WSSecurityAnonymous WSE 3.0 快速入門範例通訊的 WCF 用戶端的繫結屬性。
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Security.Cryptography.X509Certificates
Imports System.ServiceModel
Imports System.ServiceModel.Security
Imports System.ServiceModel.Channels
Imports Microsoft.VisualBasic
Namespace Microsoft.ServiceModel.Samples
' The service contract is defined in generatedClient.vb, generated from the service by
' the svcutil tool.
Class Program
Public Shared Sub Main(ByVal args As String())
CallWseService(True)
End Sub
Private Shared Sub CallWseService(ByVal usePolicyFile As Boolean)
Dim address As New EndpointAddress(New Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"), EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer"))
Dim binding As New WseHttpBinding()
If Not usePolicyFile Then
binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate
binding.EstablishSecurityContext = True
binding.RequireDerivedKeys = True
binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt
Else
binding.LoadPolicy("..\wse3policyCache.config", "ServerPolicy")
End If
Dim client As New WSSecurityAnonymousServiceSoapClient(binding, address)
' Need to supply the credentials depending on the type of WseSecurityAssertion used.
' Anonymous only requires server certificate. UsernameForCertificate would also require
' a username and password to be supplied.
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectDistinguishedName, "CN=WSE2QuickStartServer")
Dim symbols As String() = New String() {"FABRIKAM", "CONTOSO"}
Dim quotes As StockQuote() = client.StockQuoteRequest(symbols)
client.Close()
' Success!
For Each quote As StockQuote In quotes
Console.WriteLine("")
Console.WriteLine("Symbol: " + quote.Symbol)
Console.WriteLine("" & Chr(9) & "Name:" & Chr(9) & "" & Chr(9) & "" & Chr(9) & "" + quote.Name)
Console.WriteLine("" & Chr(9) & "Last Price:" & Chr(9) & "" & Chr(9) & "" & quote.Last)
Console.WriteLine("" & Chr(9) & "Previous Change:" & Chr(9) & "" & quote.PreviousChange & "%")
Next
Console.WriteLine("Press <ENTER> to terminate client.")
Console.ReadLine()
End Sub
End Class
End Namespace
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.ServiceModel.Channels;
namespace Microsoft.ServiceModel.Samples
{
// The service contract is defined in generatedClient.cs, generated from the service by
// the svcutil tool.
class Program
{
static void Main(string[] args)
{
CallWseService(true);
}
static void CallWseService(bool usePolicyFile)
{
EndpointAddress address = new EndpointAddress(new Uri("https://localhost/WSSecurityAnonymousPolicy/WSSecurityAnonymousService.asmx"),
EndpointIdentity.CreateDnsIdentity("WSE2QuickStartServer"));
WseHttpBinding binding = new WseHttpBinding();
if (!usePolicyFile)
{
binding.SecurityAssertion = WseSecurityAssertion.AnonymousForCertificate;
binding.EstablishSecurityContext = true;
binding.RequireDerivedKeys = true;
binding.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
}
else
binding.LoadPolicy("..\\wse3policyCache.config", "ServerPolicy");
WSSecurityAnonymousServiceSoapClient client = new WSSecurityAnonymousServiceSoapClient(binding, address);
// Need to supply the credentials depending on the type of WseSecurityAssertion used.
// Anonymous only requires server certificate. UsernameForCertificate would also require
// a username and password to be supplied.
client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectDistinguishedName,
"CN=WSE2QuickStartServer");
string[] symbols = new string[] { "FABRIKAM", "CONTOSO" };
StockQuote[] quotes = client.StockQuoteRequest(symbols);
client.Close();
// Success!
foreach (StockQuote quote in quotes)
{
Console.WriteLine("");
Console.WriteLine("Symbol: " + quote.Symbol);
Console.WriteLine("\tName:\t\t\t" + quote.Name);
Console.WriteLine("\tLast Price:\t\t" + quote.Last);
Console.WriteLine("\tPrevious Change:\t" + quote.PreviousChange + "%");
}
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}