我们正在开发许多托管在 IIS 中的 WCF 服务。(在 Windows Server 2003 SP2 上运行的 IIS 6.0)。这些服务是为 REST 设置的。对于环境(DEV、CERT、PROD),我们通常在每个 IIS 服务器上有许多服务。每个服务都有自己的登录帐户,通过应用程序池分配。
这很好用,但是如果我们在虚拟目录上启用 Windows 身份验证(以允许用户上下文传递,而不是模拟或委托),我们会在特定情况下遇到安全错误。如果我们使用 ServiceEndpoint 连接到 C# 代码中的服务,它可以工作,但是当我们通过浏览器或非 Wcf 代码(例如 HttpWebRequest、java 等)连接到服务时,我们会收到安全错误。
如果我将 IIS 中的身份验证从协商、NTLM 更改为 NTLM,那么它就可以工作。真正发生的事情是我们正在禁用 Kerberos,并且由于这些服务与网络中的其他服务器通信,我们开始遇到服务无法连接到远程服务器(通常是 SQL Server)的其他问题。
现在这是奇怪的部分,如果我们给机器一个 DNS 别名(CNAME 记录)并将 URL 与它的别名一起使用。例如
http://dnsalias/service/myservice.svc/foo WORKS!
but
http://machinename/service/myservice.svc/foo FAILS :(
我们不能为所有这些机器提供 DNS 别名(这一直是我们目前的解决方案),因为我们开始广泛使用虚拟机并让它们上下运转。所以我们只有机器名称,我们不想在机器启动时开始编写 DNS 别名脚本。
现在我知道了 SPN 问题,但是,由于我们在同一个网站(通常是默认网站)上托管了多个服务,我们只能为每个服务器/帐户创建 1 个服务主体名称。由于我们为每台服务器托管多个服务,每个服务都映射到自己的帐户,因此这不是解决方案。
setspn -a HTTP//WCFServer.domain.com customDomainAccount
另一个问题,我们没有在配置文件中定义端点,只在代码中,这里是获取 REST 绑定的代码。
protected internal static Binding GetWebHttpBinding()
{
// Create a new binding with Authentication enabled
var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
// Set defaults
SetTimeouts(binding);
SetReaderQuotas(binding.ReaderQuotas);
binding.MaxReceivedMessageSize = MaxReceivedMessageSize;
binding.MaxBufferSize = 65536;
// Set the Credential type to Windows to allow single sign-on
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
// Need Streamed Response since Transport security does not allow streamed requests
binding.TransferMode = TransferMode.StreamedResponse;
return binding;
}