如果需要,如何编写调用 WCF 服务并从 Kerberos 回退到 NTLM 的代码?

Posted

技术标签:

【中文标题】如果需要,如何编写调用 WCF 服务并从 Kerberos 回退到 NTLM 的代码?【英文标题】:How to write code that calls a WCF service and falls back from Kerberos to NTLM if needed? 【发布时间】:2011-04-10 00:38:32 【问题描述】:

我需要以编程方式调用 WCF 服务。该服务可能使用 NTLM 或 Kerberos 身份验证托管,并且需要在其中任何一个下工作。也就是说,如果通过 Kerberos 连接到服务失败,那么它应该回退到 NTLM。

这是我用于 Kerberos 身份验证的代码(如果相关,该服务托管在 SharePoint 2010 中并从 Web 部件调用):

public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)

    var binding = new BasicHttpBinding();
    binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
    url = url.EndsWith("/") ? url + SiteMembershipAddress : url + "/" + SiteMembershipAddress;
    var endpoint = new EndpointAddress(url);
    var proxy = new SiteMembershipSvc.SiteMembershipServiceClient(binding, endpoint);
    proxy.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
    return proxy;

在 NTLM 环境中运行时调用代理上的方法会报错:

HTTP 请求未经授权 客户端认证方案 '谈判'。身份验证标头 从服务器收到的是“NTLM”。

注意:该 URL 可能位于另一个服务器上的另一个 Web 应用程序中。我无法检查 Web 部件的 Web 应用程序在何种身份验证下运行,并假设它与 WCF 服务的托管位置相同。

我如何(自动或手动)确保身份验证在失败时从 Kerberos 回退到 NTLM?

更新:

如前所述,当调用 Web 方法时会发生身份验证错误。但是,我不想等那么久,因为服务中有几个 Web 方法是从多个地方调用的。我想在配置代理的地方测试身份验证(在上面的代码 sn-p 中)。

我尝试过使用proxy.Open(),但这似乎不会导致失败。

【问题讨论】:

【参考方案1】:

这有点离题了,但为什么要退回到 NTLM。我在活动目录和 WCF 的安全性方面都遇到了很大的困难,这些都与服务主体名称 (SPN) 相关。

如果您将服务作为网络服务以外的方式运行,Kerberos 将失败,除非您在域中为您的服务声明了 SPN。要设置 SPN,您需要 windows 服务器管理工​​具包,其中包含命令 setspn。

setspn -A HTTP\machinename domain\service_account

这将允许 Kerberos 在域内将客户端凭据共享给您的服务。

请做一些阅读,因为根据您的设置,您可能会破坏在同一机器上运行的任何其他服务的 kerberos。

【讨论】:

嗯,我希望它首先尝试 Kerberos,如果失败,然后使用 NTLM。我希望使用 Kerberos 身份验证会自动执行此操作,但似乎没有。 我的第一个假设也是如此。思考的方式是,如果企业中的任何服务是受信任的,那么您可能有一个流氓服务窃取凭据,冒充并做一些淘气的事情。 SPN 强制企业管理员说该服务是可信的,拥有您的 kerberos 凭证,否则不要要求它。 很高兴知道这一点。我已经重新构建了这个问题,因为我认为它不是很清楚。我需要 Web 部件“自动选择”正确的身份验证,具体取决于服务是托管在 Kerberos 还是 Windows 身份验证中。【参考方案2】:

(我知道原来的帖子很老了。)

你可以使用 BasicHttpBinding 以外的东西(比如 WsHttpBinding)吗?根据this 文章,BasicHttpBinding 是绑定对象的一个​​例外,因为它不会自动协商。这就是 allowNTLM 无效的原因。

【讨论】:

不知道,看起来 WsHttpBinding 可以在 SharePoint 中使用。感谢您的提示!【参考方案3】:

我有同样的错误消息,我发布了关于 here 并通过创建一个动态端点来解决它,如下所示:

public static SiteMembershipSvc.SiteMembershipServiceClient InitialiseSiteMembershipService(string url)

    //create endpoint
    EndpointAddress ep = new EndpointAddress(new Uri(string), EndpointIdentity.CreateUpnIdentity("MyDomain\WCFRunAsUser"));
    //create proxy with new endpoint
    SiteMembershipSvc.SiteMembershipServiceClient service = new SiteMembershipSvc.SiteMembershipServiceClient("wsHttp", ep);
    //allow client to impersonate user
    service.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
    //return our shiny new service
    return service;

我以特定 Active Directory 用户而不是默认的 NETWORK_SERVICE 用户身份运行 WCF 服务。

【讨论】:

这很酷,但我需要将当前用户的凭据传递给 Web 服务。【参考方案4】:

尝试设置:

proxy.ClientCredentials.Windows.AllowNTLM = true;

根据this,AllowNTLM 现已过时 - 我不确定正确的替代方案是什么。

【讨论】:

幸运的是,我使用的是 .NET 3.5,它没有被标记为过时。我会试一试 - 谢谢! 我检查了这一点,发现 AllowNTLM 默认为 true,所以很遗憾这不起作用。我已经更新了问题。【参考方案5】:

我猜你正在使用服务器的完整 dns 名称作为服务的地址。尝试使用 NETBios 名称或 IP 地址。这应该会强制它使用 NTLM。

如果您知道服务器使用的协议,您可以将您的应用配置为使用全名或 ip。

希望对你有用。

【讨论】:

你说得对,我使用的是 DNS 名称。这是我唯一的选择,因为系统是负载平衡的,我忘了提到 WCF 服务托管在 SharePoint 中 - 它只会响应该 DNS 名称上的请求。【参考方案6】:

如果您的 Kerberos 失败,它将自动默认为 NTLM,您无需执行任何特殊操作。

http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part1.html

http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part2.html

http://www.windowsecurity.com/articles/Troubleshooting-Kerberos-SharePoint-environment-Part3.html

【讨论】:

事实并非如此。如果我指定HttpClientCredentialType.Windows,我会收到问题中写的错误。【参考方案7】:

我无法找到自动执行此操作的方法。相反,我已将 UI 添加到必须选择身份验证类型的应用程序中。

【讨论】:

以上是关于如果需要,如何编写调用 WCF 服务并从 Kerberos 回退到 NTLM 的代码?的主要内容,如果未能解决你的问题,请参考以下文章

从 WCF 服务如何以当前用户而不是 IIS\DefaultApppool 的身份调用第三方 dll 中的方法

WCF 限制请求

1.WCF服务编写与调用

WCF服务需要调用异步

WCF系列教程之客户端异步调用服务

如何从 javascript 调用 WCF 服务?