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

8

我需要以编程方式调用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请求未经授权,客户端身份验证方案为“Negotiate”。从服务器接收到的身份验证标头为“NTLM”。
注意: URL可能在另一个Web应用程序上,位于另一台服务器上。无法检查Web部件的Web应用程序以何种身份验证方式运行,并假设它与WCF服务托管的位置相同。
如何确保身份验证失败时自动或手动从Kerberos回退到NTLM?
更新:
如上所述,当调用Web方法时,会出现身份验证错误。但是,由于有多个地方调用服务中的多个Web方法,我不想等待那么久。我希望在配置代理的点(在上面的代码段中)测试身份验证。
我已尝试使用`proxy.Open()`,但似乎并没有引起故障。
7个回答

1

我知道原帖非常古老。

你能否使用除BasicHttpBinding之外的其他东西(如WsHttpBinding)?根据this文章,BasicHttpBinding是绑定对象中唯一的例外,因为它不会自动协商。这就是为什么allowNTLM没有效果的原因。


不知道这一点,看起来在SharePoint中使用WsHttpBinding应该是可以的。感谢您的提示! - Alex Angas

1

这有点出乎意料,但为什么它会回退到NTLM。我在Active Directory和WCF中遇到了与服务主体名称(SPN)相关的安全性问题。

如果您将服务运行为Network Service以外的其他内容,则Kerberos将失败,除非您在域中为您的服务声明了SPN。要设置SPN,您需要Windows服务器管理工具包,其中包含setspn命令。

setspn -A HTTP\machinename domain\service_account

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

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


更确切地说,我希望首先尝试Kerberos,如果失败则使用NTLM。我希望使用Kerberos身份验证可以自动完成这个过程,但似乎并不是这样。 - Alex Angas
我的第一反应也是如此。我们可以这样考虑,如果企业中有任何一个服务被信任了,那么你就可能会遭遇到一些损失,因为一些恶意的服务可以窃取凭据、冒充身份并做出一些坏事。而SPN则可以让企业管理员明确表示该服务是受信任的,拥有你的Kerberos凭据,否则就不要请求它。 - Spence
知道这点很好。我稍微重新提出了问题,因为我认为它不太清楚。我需要Web部件根据服务是托管在Kerberos还是Windows身份验证中自动选择正确的身份验证。 - Alex Angas

0

我还没有找到自动完成此操作的方法。相反,我已经在应用程序中添加了用户界面,必须选择身份验证类型。


0

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

如果你知道服务器正在使用的协议,你可以配置你的应用程序使用完整名称或 IP。

希望这对你有用。


你说得对,我正在使用DNS名称。这是我的唯一选择,因为系统是负载平衡的,而且我忘了提到WCF服务是托管在SharePoint中的 - 它只会响应该DNS名称的请求。 - Alex Angas

0

这并不是事实。如果我指定 HttpClientCredentialType.Windows,我会得到问题中写的错误。 - Alex Angas

0

尝试设置:

proxy.ClientCredentials.Windows.AllowNTLM = true;

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


幸运的是,我正在使用不被标记为过时的.NET 3.5。我会尝试一下 - 谢谢! - Alex Angas
我检查了一下,发现AllowNTLM默认为true,所以不幸的是这个方法行不通。我已经更新了问题。 - Alex Angas

0

我遇到了与这里发布的相同的错误消息,并通过创建动态端点来解决它,如下所示:

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;
}

我将 WCF 服务运行在特定的 Active Directory 用户下,而不是默认的 NETWORK_SERVICE。


很酷,但我需要通过当前用户的凭据传递给 Web 服务。 - Alex Angas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接