如何通过 REST 和 Windows 身份验证在 WCF 服务中使用计算机名称
我们正在开发许多托管在 IIS 中的 WCF 服务。 (IIS 6.0 在 Windows Server 2003 SP2 上运行)。这些服务是针对 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;
}
We are developing many WCF services that are hosted in IIS. (IIS 6.0 running on Windows Server 2003 SP2). These services are set up for REST. For an environment (DEV, CERT, PROD), we typically have many services per IIS server. Each service has its own login account that's assigned via the Application Pool.
This works fine, but if we enable Windows Authentication on the virtual directory (to allow user context passing, not for impersonation or delegation), we get security errors in specific cases. If we connect to the service in C# code using ServiceEndpoint it works, however when we connect to the services via browser or non Wcf code (e.g. HttpWebRequest, java, et al) we get the security errors.
If I change the authentication in IIS from Negotiate, NTLM to just NTLM, then it works.
Really what's happening is we're disabling Kerberos, and since these services talk to other servers in the network, we start to get other issues where services can't connect to remote servers (typically SQL Servers).
Now here's the weird part, if we give the machine a DNS alias (CNAME record) and use the URL with the alias it works.
e.g.
http://dnsalias/service/myservice.svc/foo WORKS!
but
http://machinename/service/myservice.svc/foo FAILS :(
We cannot give all these machines DNS aliases (that has been our solution to this point) because we're starting to use VMs extensively and spinning them up and down. So we only have machine names, we don't want to start scripting DNS aliases on machine spin up.
Now I know the SPN issue, however, since we have multiple services hosted on the same website (Default website typically), we can only create 1 service principal name per server/account. Since we host multiple services per server each mapped to its own account, this isn't a solution.
setspn -a HTTP//WCFServer.domain.com customDomainAccount
Another wrinkle, we don't define endpoints in config files, only in code, here is the code to get a REST binding.
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
好吧,很久以后,我带着答案回来了。对于所描述的环境,唯一的答案是使用 NTLM 身份验证。致电 Microsoft 支持人员已证实这一点。
Ok, much much later, I'm back with the answer. For the environment described, the only answer is to use NTLM authentication. This has been confirmed with a call to Microsoft support.
我了解到机器名称已经是一个 DNS 条目。机器的原始名称/或查找实际上是它的 IP 地址。因此,当您在 url 中声明“machinename”不起作用时,这不是已经是一个 DNS 条目了吗?如果您 ping 'machinename',显示响应 ping 的 IP 是什么?那应该是机器的IP。
对我来说,这听起来像是您正在为 DNS 查找创建 DNS 查找。你能澄清一下吗?
我可以确认,当我在域上进行 DNS 查找以查找我正在使用的 WCF 服务或站点的 IP 时,奇迹也会发生在我身上,所以我知道您来自哪里。
我不知道您的客户端代码是什么样的,但请查看以下链接以进行匹配。在调用 RESTful 服务之前,您必须建立网络凭证。如果这样做,是否可以解决必须禁用 kerberos 的另一个问题?
I had the understanding that the machine name is a DNS entry already. The raw name/or lookup of a machine is actually its IP address. So when you state 'machinename' in your url that does not work, isn't that already a DNS entry? If you ping 'machinename' what is the IP that shows responding to the ping; that should be the IP of the machine.
To me it sounds like you are creating a DNS lookup for a DNS lookup. Can you clarify on this a bit?
I can confirm that magic happens for me too, once I get a DNS lookup on the domain for an IP of a WCF service or site I am working with, so I understand where you are coming from.
I don;t know what your client code looks like, but take a look at the following link to match up. You have to build up a Network Credential before calling the RESTful service. If you do this, does it solve the other issue with having to diasble kerberos?
我在集成 Windows 身份验证和 WebHttpBinding 尝试利用 WebHttpSecurityMode.TransportCredentialOnly 时也遇到了类似的“安全错误”。但需要 SSL,所以我能够坚持使用 WebHttpSecurityMode.Transport > 同时还配置 HttpClientCredentialType.Windows。
I was also getting similar "security errors" with Integrated Windows Authentication and WebHttpBinding trying to utilize WebHttpSecurityMode.TransportCredentialOnly .. but was requiring SSL so I was able to stick to WebHttpSecurityMode.Transport while also configuring HttpClientCredentialType.Windows.