重写服务标识以便进行身份验证
通常情况下不需要设置服务上的标识,因为客户端凭据类型的选择即规定了服务元数据中公开的标识的类型。例如,下面的配置代码使用 <wsHttpBinding>元素并将 clientCredentialType 属性设置为 Windows。
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_Windows">
<security mode="Message">
<message clientCredentialType="Windows"
establishSecurityContext="false"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<!-- other configuration code not shown -->
</system.serviceModel>
</configuration>
下面的 Web 服务描述语言 (WSDL) 片断演示前面定义的终结点的标识。在本示例中,服务作为自承载服务在特定的用户帐户 (username@contoso.com) 下运行,因此用户主要名称 (UPN) 标识包含该帐户名。在 Windows 域中,UPN 也称为用户登录名。
<wsdl:service name="CalculatorService">
<wsdl:port name="WSHttpBinding_ICalculator_Windows"
binding="tns:WSHttpBinding_ICalculator_Windows">
<soap12:address
location=
"https://localhost:8003/servicemodelsamples/service/upnidentity" />
<wsa10:EndpointReference>
<wsa10:Address>
https://localhost:8003/servicemodelsamples/service/upnidentity
</wsa10:Address>
<Identity
xmlns="https://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
<Upn>username@contoso.com</Upn>
</Identity>
</wsa10:EndpointReference>
</wsdl:port>
</wsdl:service>
有关演示标识设置的示例应用程序,请参见服务标识示例。有关服务标识的更多信息,请参见服务标识和身份验证。
Kerberos 身份验证和标识
默认情况下,在将服务配置为使用 Windows 凭据时,会在 WSDL 中生成一个包含 <userPrincipalName> 或 <servicePrincipalName> 元素的 <identity> 元素。如果服务正在 LocalSystem、LocalService 或 NetworkService 帐户下运行,则默认情况下会生成一个 host/<主机名> 形式的服务主体名称 (SPN),因为这些帐户有权访问计算机的 SPN 数据。如果服务正在其他帐户下运行,则 Windows Communication Foundation (WCF) 会生成一个 <用户名>@<域名> 形式的 UPN。发生这种情况的原因是 Kerberos 身份验证要求向客户端提供 UPN 或 SPN,以便对服务进行身份验证。
您还可以使用 Setspn.exe 工具向域中服务的帐户注册其他 SPN。然后,可以使用该 SPN 作为服务的标识。若要下载该工具,请参见 Windows 2000 Resource Kit Tool : Setspn.exe(Windows 2000 资源工具包工具:Setspn.exe)。有关该工具的更多信息,请参见 Setspn Overview(Setspn 概述)。
注意: |
---|
若要使用不带协商的 Windows 凭据类型,服务的用户帐户必须有权访问向 Active Directory 域注册的 SPN。可以使用下列方式来实现: |
使用 NetworkService 或 LocalSystem 帐户运行服务。由于这些帐户有权访问在计算机加入 Active Directory 域时建立的计算机 SPN,因此 WCF 将自动在服务的元数据 (WSDL) 中的服务终结点内生成正确的 SPN 元素。
使用任意 Active Directory 域帐户运行服务。在这种情况下,需要为该域帐户建立一个 SPN,这可以使用 Setspn.exe 实用工具来完成。为服务的帐户创建 SPN 后,配置 WCF 以通过服务的元数据 (WSDL) 将该 SPN 发布到服务的客户端。这可以通过为公开的终结点设置终结点标识(借助于应用程序配置文件或代码)来完成。
有关 SPN、Kerberos 协议和 Active Directory 的更多信息,请参见 Kerberos Technical Supplement for Windows(面向 Windows 的 Kerberos 技术补充)。
当 SPN 或 UPN 等于空字符串时
如果将 SPN 或 UPN 设置为等于空字符串,将出现多种不同的情况,具体取决于所使用的安全级别和身份验证模式:
如果使用传输级安全,则会选择 NT LanMan (NTLM) 身份验证。
如果使用消息级安全,则根据身份验证模式的不同,身份验证可能会失败:
如果使用 spnego 模式,并且 AllowNtlm 属性设置为 false,则身份验证将失败。
在使用 spnego 模式并且 AllowNtlm 属性设置为 true 的情况下,如果 UPN 为空,则身份验证将失败;如果 SPN 为空,则身份验证将成功。
如果使用 Kerberos direct(也称为“一次完成”),则身份验证将失败。
在配置中使用 <identity> 元素
如果将前面演示的绑定中的客户端凭据类型更改为证书,则生成的 WSDL 将包含一个 Base64 序列化 X.509 证书作为标识值,如下面的代码所示。这是除 Windows 之外的所有客户端凭据类型的默认值。
<Identity xmlns="https://schemas.xmlsoap.org/ws/2006/02/addressingidentity">
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<X509Data>
<X509Certificate>MIIBxjCCAXSgAwIBAgIQmXJgyu9tro1M98GifjtuoDAJBgUrDgMCHQUAMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MB4XDTA2MDUxNzIxNDQyNVoXDTM5MTIzMTIzNTk1OVowKTEQMA4GA1UEChMHQ29udG9zbzEVMBMGA1UEAxMMaWRlbnRpdHkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBmivcb8hYbh11hqVoDuB7zmJ2y230f/b4e+4P6yXtKKuhUdYcIqc8mAforIM4WWJEVGeJVq9sFEwqrL5Ryid8jMTRwPLvA/x/wvj1gtD1GWJ+aUh2pqieiGL7MWTepHAQBIibUxgOrAOz0j9Xhg0iDFYScdYUjeqI3yZIDC7WbwIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwCQYFKw4DAh0FAANBADB/J2QjdSPL8Doj3pAveCXd/5fY03eo9kUym/Tmb4ubdqsObri0qnYR/n8Wxsa1yJ4Dks6cNBTPS4l5B7zUeNo=</X509Certificate>
</X509Data>
</KeyInfo>
</Identity>
通过在配置中使用 <identity> 元素或在代码中设置标识,可以更改默认服务标识值或更改该标识的类型。下面的配置代码使用值 contoso.com
设置域名系统 (DNS) 标识。
以编程方式设置标识
您的服务不必显式指定标识,因为 WCF 会自动确定标识。但是,如果需要,WCF 允许您指定终结点的标识。下面的代码使用特定的 DNS 标识添加了一个新的服务终结点。
Dim ep As ServiceEndpoint = myServiceHost.AddServiceEndpoint(GetType(ICalculator), New WSHttpBinding(), String.Empty)
Dim myEndpointAdd As New EndpointAddress(New Uri("https://localhost:8088/calc"), EndpointIdentity.CreateDnsIdentity("contoso.com"))
ep.Address = myEndpointAdd
ServiceEndpoint ep = myServiceHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(),
String.Empty);
EndpointAddress myEndpointAdd = new EndpointAddress(new Uri("https://localhost:8088/calc"),
EndpointIdentity.CreateDnsIdentity("contoso.com"));
ep.Address = myEndpointAdd;