CustomMexEndpoint 示例演示如何使用非元数据交换绑定之一的安全元数据终结点实现服务,以及如何配置 ServiceModel 元数据实用工具工具(Svcutil.exe)或客户端从此类元数据终结点中提取元数据。 有两个系统提供的绑定可用于公开元数据终结点:mexHttpBinding 和 mexHttpsBinding。 mexHttpBinding 用于以不安全的方式通过 HTTP 公开元数据终结点。 mexHttpsBinding 用于以安全方式通过 HTTPS 公开元数据终结点。 此示例演示如何使用WSHttpBinding公开安全的元数据端点。 如果要更改绑定上的安全设置,但不想使用 HTTPS,需要执行此作。 如果使用 mexHttpsBinding,则元数据终结点是安全的,但无法修改绑定设置。
注释
本示例的设置过程和生成说明位于本主题末尾。
服务
此示例中的服务有两个终结点。 应用程序终结点可用于 ICalculator
上的 WSHttpBinding
协定,其中启用了 ReliableSession
并且 Message
安全性为使用证书。 元数据终结点也使用WSHttpBinding
,其安全设置相同,但没有使用ReliableSession
。 下面是相关配置:
<services>
<service name="Microsoft.ServiceModel.Samples.CalculatorService"
behaviorConfiguration="CalculatorServiceBehavior">
<!-- use base address provided by host -->
<endpoint address=""
binding="wsHttpBinding"
bindingConfiguration="Binding2"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
<endpoint address="mex"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
contract="IMetadataExchange" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
<binding name="Binding2">
<reliableSession inactivityTimeout="00:01:00" enabled="true" />
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
在许多其他示例中,元数据终结点使用默认值 mexHttpBinding
,这是不安全的。 下面是使用具有 WSHttpBinding
安全性的 Message
来保护的元数据。 为了使元数据客户端检索此元数据,必须使用匹配的绑定进行配置。 此示例演示了两个此类客户端。
第一个客户端使用 Svcutil.exe 提取元数据,并在设计时生成客户端代码和配置。 由于服务对元数据使用非默认绑定,因此必须专门配置 Svcutil.exe 工具,以便可以使用该绑定从服务获取元数据。
第二个客户端使用 MetadataResolver
动态提取已知合同的元数据,然后在动态生成的客户端上调用操作。
Svcutil 客户端
使用默认绑定托管 IMetadataExchange
终结点时,可以通过使用该终结点的地址来运行 Svcutil.exe。
svcutil http://localhost/servicemodelsamples/service.svc/mex
并且它有效。 但在此示例中,服务器使用非默认终结点来托管元数据。 因此,必须指示 Svcutil.exe 使用正确的绑定。 可以使用 Svcutil.exe.config 文件完成此作。
Svcutil.exe.config 文件类似于普通客户端配置文件。 唯一不寻常的方面是客户端终结点名称和协定:
<endpoint name="http"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
behaviorConfiguration="ClientCertificateBehavior"
contract="IMetadataExchange" />
终结点名称必须是托管元数据的地址方案的名称,并且终结点协定必须是 IMetadataExchange
。 因此,当使用如下所示的命令行运行 Svcutil.exe 时:
svcutil http://localhost/servicemodelsamples/service.svc/mex
它查找名为“http”的终结点和协定 IMetadataExchange
,以配置与元数据终结点的通信交换的绑定和行为。 示例中 Svcutil.exe.config 文件的其余部分指定绑定配置和行为凭据,以匹配服务器的元数据终结点配置。
为了使 Svcutil.exe 在 Svcutil.exe.config中选取配置,Svcutil.exe 必须与配置文件位于同一目录中。 因此,必须将 Svcutil.exe 从其安装位置复制到包含 Svcutil.exe.config 文件的目录。 然后,从该目录中运行以下命令:
.\svcutil.exe http://localhost/servicemodelsamples/service.svc/mex
前导“.\”确保运行此目录(具有相应的 Svcutil.exe.config 的目录)中的 Svcutil.exe 副本。
MetadataResolver 客户端
如果客户端知道契约以及如何在设计时与元数据进行对话,客户端可以使用 MetadataResolver
动态查找应用程序终结点的绑定和地址。 此示例客户端演示了如何通过创建和配置MetadataExchangeClient
来配置MetadataResolver
所使用的绑定和凭据。
Svcutil.exe.config 中显示的同一绑定和证书信息可以在以下项 MetadataExchangeClient
上命令性地指定:
// Specify the Metadata Exchange binding and its security mode
WSHttpBinding mexBinding = new WSHttpBinding(SecurityMode.Message);
mexBinding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
// Create a MetadataExchangeClient for retrieving metadata, and set the // certificate details
MetadataExchangeClient mexClient = new MetadataExchangeClient(mexBinding);
mexClient.SoapCredentials.ClientCertificate.SetCertificate( StoreLocation.CurrentUser, StoreName.My,
X509FindType.FindBySubjectName, "client.com");
mexClient.SoapCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
mexClient.SoapCredentials.ServiceCertificate.SetDefaultCertificate( StoreLocation.CurrentUser, StoreName.TrustedPeople,
X509FindType.FindBySubjectName, "localhost");
配置 mexClient
后,我们可以枚举我们感兴趣的协定,并使用 MetadataResolver
获取包含这些协定的终结点列表。
// The contract we want to fetch metadata for
Collection<ContractDescription> contracts = new Collection<ContractDescription>();
ContractDescription contract = ContractDescription.GetContract(typeof(ICalculator));
contracts.Add(contract);
// Find endpoints for that contract
EndpointAddress mexAddress = new EndpointAddress(ConfigurationManager.AppSettings["mexAddress"]);
ServiceEndpointCollection endpoints = MetadataResolver.Resolve(contracts, mexAddress, mexClient);
最后,可以使用这些终结点中的信息来初始化绑定和地址,以创建与应用程序终结点通信的通道ChannelFactory
。
ChannelFactory<ICalculator> cf = new ChannelFactory<ICalculator>(endpoint.Binding, endpoint.Address);
此示例客户端的关键点是演示,使用 MetadataResolver
时,必须为元数据交换通信指定自定义绑定或行为,可以使用 MetadataExchangeClient
来指定这些自定义设置。
设置和生成示例
确保已为 Windows Communication Foundation 示例 执行One-Time 安装过程。
要生成解决方案,请按照生成 Windows Communication Foundation 示例中的说明进行操作。
在同一计算机上运行示例
从示例安装文件夹运行 Setup.bat。 这会安装运行示例所需的所有证书。 请注意,Setup.bat 使用 FindPrivateKey.exe 工具,该工具通过从针对 Windows Communication Foundation 的一次性设置程序示例运行 setupCertTool.bat 安装。
从 \MetadataResolverClient\bin 或 \SvcutilClient\bin 运行客户端应用程序。 客户端活动显示在客户端控制台应用程序中。
完成示例后,通过运行 Cleanup.bat 来删除证书。 其他安全示例使用相同的证书。
跨计算机运行示例
在服务器上,运行
setup.bat service
。 如果采用service
参数运行setup.bat
,则使用计算机的完全限定域名创建一个服务证书,并将此服务证书导出到名为 Service.cer 的文件中。在服务器上,编辑 Web.config 以反映新的证书名称。 也就是说,将 <serviceCertificate> 元素中的
findValue
属性更改为计算机的完全限定域名。将Service.cer文件从服务目录复制到客户端计算机上的客户端目录。
在客户端上运行
setup.bat client
。 使用client
参数运行setup.bat
将创建名为 Client.com 的客户端证书,并将客户端证书导出到名为Client.cer的文件。在客户端计算机上的
MetadataResolverClient
的 App.config 文件中,更改 Mex 终结点的地址值以与服务的新地址相匹配。 通过使用服务器的完全限定域名替换 localhost 来执行此操作。 还要将 metadataResolverClient.cs 文件中出现的“localhost”更改为新的服务证书名称(服务器的完全限定域名)。 对 SvcutilClient 项目的 App.config 执行相同的操作。将Client.cer文件从客户端目录复制到服务器上的服务目录。
在客户端上运行
ImportServiceCert.bat
。 这会将服务证书从 Service.cer 文件导入 CurrentUser - TrustedPeople 存储中。在服务器上,运行
ImportClientCert.bat
,这会将客户端证书从 Client.cer 文件导入 LocalMachine - TrustedPeople 存储。在服务计算机上,在 Visual Studio 中生成服务项目,并在 Web 浏览器中选择帮助页以验证它是否正在运行。
在客户端计算机上,从 VS 运行 MetadataResolverClient 或 SvcutilClient。
运行示例后进行清理
运行完示例后,在示例文件夹中运行 Cleanup.bat。
注释
在跨计算机运行此示例时,此脚本不会删除客户端上的服务证书。 如果您运行了在多个计算机上使用证书的 Windows Communication Foundation (WCF) 示例,请务必清除安装在当前用户 - TrustedPeople 存储中的服务证书。 为此,请使用以下命令:
certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name>
例如:certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com
。