重写服务标识以便进行身份验证
通常情况下不需要设置服务上的标识,因为客户端凭据类型的选择即规定了服务元数据中公开的标识的类型。 例如,下面的配置代码使用 <wsHttpBinding> 元素并将 clientCredentialType
属性设置为 Windows。
下面的 Web 服务描述语言 (WSDL) 片断演示前面定义的终结点的标识。 在本示例中,服务作为自承载服务在特定用户帐户 (username@contoso.com
) 下运行,因此用户主体名称 (UPN) 标识包含该帐户名。 在 Windows 域中,UPN 也称为用户登录名。
有关演示标识设置的示例应用程序,请参阅服务标识示例。 有关服务标识的详细信息,请参阅服务标识和身份验证。
Kerberos 身份验证和标识
默认情况下,当服务配置为使用 Windows 凭据时,WSDL 中将生成包含 <userPrincipalName> 或 <servicePrincipalName> 元素的 <identity> 元素。 如果服务正在 LocalSystem
、LocalService
或 NetworkService
帐户下运行,则默认情况下会生成一个 host/
<hostname> 形式的服务主体名称 (SPN),因为这些帐户有权访问计算机的 SPN 数据。 如果服务正在其他帐户下运行,则 Windows Communication Foundation (WCF) 会生成一个 <username>@<domainName>
形式的 UPN。 发生这种情况的原因是 Kerberos 身份验证要求向客户端提供 UPN 或 SPN,以便对服务进行身份验证。
还可以使用 Setspn 工具向域中的服务帐户注册其他 SPN。 然后,可以使用该 SPN 作为服务的标识。 有关运行该工具的详细信息,请参阅 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 的详细信息,请参阅针对 Windows 的 Kerberos 技术补充信息。
当 SPN 或 UPN 等于空字符串时
如果将 SPN 或 UPN 设置为等于空字符串,将出现多种不同的情况,具体取决于所使用的安全级别和身份验证模式:
如果使用传输级安全,则会选择 NT LanMan (NTLM) 身份验证。
如果使用消息级安全,则根据身份验证模式的不同,身份验证可能会失败:
如果使用的是
spnego
模式,并且AllowNtlm
属性设置为false
,则身份验证将失败。在使用
spnego
模式并且AllowNtlm
属性设置为true
的情况下,如果 UPN 为空,则身份验证将失败;但如果 SPN 为空,则身份验证将成功。如果使用 Kerberos direct(也称为“一次完成”),则身份验证将失败。
在配置中使用 <identity> 元素
如果将前面演示的绑定中的客户端凭据类型更改为 Certificate
,则生成的 WSDL 将包含一个针对标识值的 Base64 序列化 X.509 证书,如下面的代码所示。 这是除 Windows 之外的所有客户端凭据类型的默认值。
通过在配置中使用 <identity>
元素或在代码中设置标识,可以更改默认服务标识的值或更改该标识的类型。 下面的配置代码使用值 contoso.com
设置域名系统 (DNS) 标识。
以编程方式设置标识
服务不必显式指定标识,因为 WCF 会自动确定标识。 但是,必要时,WCF 允许在终结点上指定标识。 下面的代码使用特定的 DNS 标识添加了一个新的服务终结点。
ServiceEndpoint ep = myServiceHost.AddServiceEndpoint(
typeof(ICalculator),
new WSHttpBinding(),
String.Empty);
EndpointAddress myEndpointAdd = new EndpointAddress(new Uri("http://localhost:8088/calc"),
EndpointIdentity.CreateDnsIdentity("contoso.com"));
ep.Address = myEndpointAdd;
Dim ep As ServiceEndpoint = myServiceHost.AddServiceEndpoint(GetType(ICalculator), New WSHttpBinding(), String.Empty)
Dim myEndpointAdd As New EndpointAddress(New Uri("http://localhost:8088/calc"), EndpointIdentity.CreateDnsIdentity("contoso.com"))
ep.Address = myEndpointAdd