除非客户端和服务器使用相同的 Windows 身份,否则带有 sspi 的 WCF Net.tcp 会失败

Posted

技术标签:

【中文标题】除非客户端和服务器使用相同的 Windows 身份,否则带有 sspi 的 WCF Net.tcp 会失败【英文标题】:WCF Net.tcp with sspi fails unless client and server using same windows Identity 【发布时间】:2013-03-14 09:30:17 【问题描述】:

我有一个由 Windows 服务托管的 WCF 服务。如果我使用与运行服务相同的凭据登录客户端计算机,则客户端应用程序成功,但如果我使用任何其他有效域帐户登录,它会失败并出现异常。

我有两个正在测试的帐户,一个是普通用户帐户,另一个帐户是管理员帐户。我已经尝试了下面列出的所有四种组合:

                   Server account
   CLient      RegUser    AdminAcct   
  RegUser     Succeeds      Fails   
 AdminAcct     Fails       Succeeds    

正如您所见,这不是管理员问题,因为当客户端和服务器都在非管理员帐户下运行时,系统可以正常工作。 在这两种失败的情况下,我都会在客户端上遇到相同的异常,但没有任何迹象表明服务器日志中发生了任何事情:

“对 SSPI 的调用失败。请参阅内部异常”

内部异常是“目标原理名称不正确。”

我已将帐户注册为 SPN。

该问题仅在我的客户端应用程序中出现,但在我使用 Visual Studio 附带的 WCVFTestClient.exe 时不会出现。

WCF 跟踪日志中的异常是

“System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel,版本=4.0.0.0,文化=中性, PublicKeyToken=b77a5c561934e089"

带有消息:

“远程端身份验证失败(流可能仍可用于其他身份验证尝试)。”

堆栈跟踪位于底部: 怎么了?

堆栈跟踪


System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(流 流、SecurityMessageProperty 和 remoteSecurity) System.ServiceModel.Channels.StreamSecurityUpgradeAcceptorBase.AcceptUpgrade(流 溪流) System.ServiceModel.Channels.InitialServerConnectionReader.UpgradeConnection(IConnection 连接,StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts) System.ServiceModel.Channels.ServerSessionPreambleConnectionReader.ServerFramingDuplexSessionChannel.OnOpen(时间跨度 暂停) System.ServiceModel.Channels.CommunicationObject.Open(时间跨度 暂停) System.ServiceModel.Dispatcher.ChannelHandler.OpenAndEnsurePump() System.Runtime.ActionItem.DefaultActionItem.TraceAndInvoke() System.Runtime.ActionItem.CallbackHelper.InvokeWithoutContext(对象 状态) System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped) System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 错误,UInt32 bytesRead,NativeOverlapped* nativeOverlapped) System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

【问题讨论】:

【参考方案1】:

找到了答案。我的问题是两个因素的结合。

    当使用 net.tcp 二进制 WCF 协议时,客户端安全模式确定 NTLM 还是 Kerberos 用于身份验证。如果您将客户端安全模式设置为“传输”,则身份验证使用 NTLM,并且只有一跳是可能的。如果您尝试让 WCF 服务器与第三台服务器(如数据库)通信,它将失败。使用 SecurityMode = "Message", otoh 会导致 WCF 服务器使用 Kerberos,它允许多个跃点...

    第二个问题与我在绑定中对客户端所做的操作有关。 WCF 协议 net.tcp 要求在客户端上实例化终结点时,必须指定“终结点标识”(参见下面的代码)。我错误地认为这与身份验证有关,因此是客户端上当前登录用户(Windows 主体)的身份。

        var epId = EndpointIdentity.CreateUpnIdentity(userPrincipalName);
        var ep = new EndpointAddress(new Uri(url), epId):
    

    否... 在客户端上创建端点时必须指定的身份必须是服务器正在运行的身份。这就是为什么每当我使用与运行该服务的用户相同的用户登录到客户端时,代码就可以工作,而当客户端是不同的用户时,代码就会失败。

    我仍然不明白为什么必须在客户端的端点中指定此用户身份(服务帐户的)。服务器上的什么功能需要这些数据?

【讨论】:

非常感谢您的回答。经过数小时的尝试和错误,我尝试了这个解决方案,它就像一个魅力!如果我们能见面,我会给你一杯啤酒;-) 不客气!我去哪里买啤酒? 根据你的个人资料,我想我们不会在不久的将来见面......但永无止境的感谢会跟随你;-) #2 是失败的主要原因。使用 Windows 身份验证时,还会验证服务器的身份。如果您将服务作为 SYSTEM 或 NETWORK SERVICE 运行,则它可以访问机器 SPN,并且您不需要显式提供 UPN,因为它将默认期待机器 SPN。由于您运行的是自定义帐户,因此您需要将其告知您的客户,以便它可以对服务器进行身份验证 #1 说您需要消息安全性才能与数据库通信是错误的,因为这些通信没有使用 WCF。它可能会触发 Kerberos,这对您来说可能是个问题。强制 Kerberos 的更好方法是将 AllowNtlm 设置为 false(谷歌搜索)。【参考方案2】:

关于第二点,客户端不需要运行在与服务器相同的Windows账户下才能成功认证,也不需要在请求中手动指定账户名。据我了解,WCF 身份验证是相互的,这意味着客户端也在验证服务器。

您收到的错误可能是由于客户端故障。如果您将客户端的服务端点配置为提供 ServicePrincipalName,则问题可能会得到解决。我发现this article 对解决我遇到的同样问题很有帮助。

【讨论】:

链接无效

以上是关于除非客户端和服务器使用相同的 Windows 身份,否则带有 sspi 的 WCF Net.tcp 会失败的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET 2.0 中的 Windows 身份验证

windows server 2008 R2 ipsec 部署方案求助

使用 Windows 身份验证的 IIS 托管 WCF 服务和 SQL 查询

如何将 Windows 身份验证凭据从客户端传递到 Web API 服务

从令牌服务器(身份服务器 4)进行身份验证后重定向到客户端的相同 URL

ADAL AcquireToken Windows 身份验证 UWP