WCF 的问题 + NTLM 身份验证和负载平衡

发布于 2024-12-05 17:08:45 字数 2969 浏览 0 评论 0原文

再次尝试让我的 WCF 服务在我们的负载平衡环境中工作,我一直充满希望。我已开始使用 因为 建议是设置keepAliveEnabled指令。

也就是说,我在服务器端设置 Windows 身份验证时遇到问题,因为 似乎没有以相同的方式运行。

服务器端看起来像这样:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<bindings>
  <customBinding>
    <binding name="HttpBinding" closeTimeout="00:00:45">
      <textMessageEncoding>
        <readerQuotas maxStringContentLength="200000" maxArrayLength="200000" />
      </textMessageEncoding>
      <httpTransport keepAliveEnabled="false" maxReceivedMessageSize="200000" authenticationScheme="Negotiate"/>
    </binding>
  </customBinding>
</bindings>
<services>
    <endpoint address="http://svcserv/Services/ReportService/Reports.svc" binding="customBinding"
              bindingConfiguration="HttpBinding" contract="ReportService.IReports" >
    </endpoint>
    <endpoint address="mex" binding="customBinding" bindingConfiguration="HttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="ReportService.ReportsBehavior">
      <serviceMetadata httpGetEnabled="true"  />
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>
</behaviors>
</system.serviceModel>

客户端看起来像这样:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<bindings>
  <basicHttpBinding>
    <binding name="CustomBinding_IReports" maxReceivedMessageSize="200000">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Windows"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint name="CustomBinding_IReports" address="http://omsnetdev/Services/ReportService/Reports.svc"
            binding="basicHttpBinding" bindingConfiguration="CustomBinding_IReports" contract="ReportService.IReports">
    <identity>
      <servicePrincipalName value="host/svcserv"/>
      <dns value="svcserv"/>
    </identity>
  </endpoint>
</client>
</system.serviceModel>

如果我将authenticationScheme 设置为“Negotiate”,那么我会收到类似于 SOAP header Action was notunderstand. 的内容。 或者,有一点,客户端发现响应内容类型为“”,但预期为“application/soap+xml”。 如果我将authenticationScheme更改为“Ntlm”,那么我会收到异常:请求失败,HTTP状态401:未经授权。但我相信这是由于协商失败(由于 SPN 值?),因此它回退到“Ntlm”并失败。

我会将此作为 IIS 服务器上的配置注销,但我已经验证了这些设置。

Once again attempting to get my WCF service working in our load balanced environment and I've been hopeful. I've moved on to using a <customBinding> since it appears that the recommendation is to set the keepAliveEnabled directive.

That said, I am having a problem setting up Windows authentication on the server side since the <customBinding> does not seem to function in the same way.

The server side looks like this:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<bindings>
  <customBinding>
    <binding name="HttpBinding" closeTimeout="00:00:45">
      <textMessageEncoding>
        <readerQuotas maxStringContentLength="200000" maxArrayLength="200000" />
      </textMessageEncoding>
      <httpTransport keepAliveEnabled="false" maxReceivedMessageSize="200000" authenticationScheme="Negotiate"/>
    </binding>
  </customBinding>
</bindings>
<services>
    <endpoint address="http://svcserv/Services/ReportService/Reports.svc" binding="customBinding"
              bindingConfiguration="HttpBinding" contract="ReportService.IReports" >
    </endpoint>
    <endpoint address="mex" binding="customBinding" bindingConfiguration="HttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="ReportService.ReportsBehavior">
      <serviceMetadata httpGetEnabled="true"  />
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>
</behaviors>
</system.serviceModel>

The client side looks like this:

<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<bindings>
  <basicHttpBinding>
    <binding name="CustomBinding_IReports" maxReceivedMessageSize="200000">
      <security mode="TransportCredentialOnly">
        <transport clientCredentialType="Windows"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint name="CustomBinding_IReports" address="http://omsnetdev/Services/ReportService/Reports.svc"
            binding="basicHttpBinding" bindingConfiguration="CustomBinding_IReports" contract="ReportService.IReports">
    <identity>
      <servicePrincipalName value="host/svcserv"/>
      <dns value="svcserv"/>
    </identity>
  </endpoint>
</client>
</system.serviceModel>

If I leave the authenticationScheme set to "Negotiate" then I receive something along the lines of either SOAP header Action was not understood. or, at one point, Client found response content type of '', but expected 'application/soap+xml'.
If I change the authenticationScheme to "Ntlm" then I receive Exception: The request failed with HTTP status 401: Unauthorized. but I believe this is due to the negotiation failing (due to the SPN value?) so it falls back to "Ntlm" and fails.

I would write this off as a configuration on the IIS server but I have verified the settings.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

红焚 2024-12-12 17:08:45

我相信这不仅仅是 WCF 问题,而且是一个概念模型。您希望实现完全无状态的方案,但同时需要对身份验证进行有状态处理,因为 NTLM(并且没有其他传输级别身份验证)是在单个请求响应中执行的。

以下是握手工作原理的简短描述:

      Client                                                Server
-----------------------------------------------------------------------------------------
Send initial request ---------------------------->
                     <----------------------------  Returns 401 with WWW-Authenticate 
                                                    header demanding NTLM
Sends empty request  ---------------------------->  
with Authorization                                
header with initial 
token                                    
                     <----------------------------  Returns 401 with WWW-Authenticate 
                                                    header containing some server token
Sends request with   ---------------------------->  
Authorization header                                
with final token                                    
                     <----------------------------  Returns 200 and expected response

此握手必须使用单个负载平衡服务器执行,但是一旦关闭持久 HTTP 连接,您将强制客户端为每个调用打开新的 TCP 连接,并且每个调用都单独进行负载平衡。这很可能会以这些调用传递到不同的服务器而结束 =>认证失败。简而言之,您需要:

  • 在 HTTP 之上运行的更高级别的负载平衡(将使用负载平衡器完成身份验证)并且对服务的调用将是匿名的
  • 另一种身份验证技术,在消息中传递凭据
  • 持久连接以强制请求路由到正确的服务器。如果您在 IIS 中托管服务,您可以将保持活动间隔减少到几秒,并希望这将提高负载平衡的效率

I belief this is not only WCF problem but a conceptual model. You want to make completely stateless scenario but in the same time you need stateful handling of authentication because NTLM (and no other transport level authentication) is performed within single request response.

Here is short description how the handshake works:

      Client                                                Server
-----------------------------------------------------------------------------------------
Send initial request ---------------------------->
                     <----------------------------  Returns 401 with WWW-Authenticate 
                                                    header demanding NTLM
Sends empty request  ---------------------------->  
with Authorization                                
header with initial 
token                                    
                     <----------------------------  Returns 401 with WWW-Authenticate 
                                                    header containing some server token
Sends request with   ---------------------------->  
Authorization header                                
with final token                                    
                     <----------------------------  Returns 200 and expected response

This handshake must be performed with single load balanced server but once you turn off persistent HTTP connections you will force your client to open new TCP connection for each call and each call is load balanced separately. That will most probably ends with these calls passed to different servers => authentication failed. In short you need either:

  • Higher level load balancing operating on top of HTTP (authentication will be done with load balancer) and calls to service will be anonymous
  • Another authentication technique passing credentials in message
  • Persistent connections to force request routing to correct server. If you are hosting services in IIS you can reduce keep alive interval to few seconds and hope that this will improve load balancing effectivity
好倦 2024-12-12 17:08:45

如果您遇到与 SPN 相关的问题,请参阅此答案:SO WCF-Security-Problem 问题

使用负载均衡器时,您应该遇到的唯一问题是您是否需要会话保持对一台主机的“粘性”。对于给定的会话,如果您配置正确,负载均衡器应该能够为您执行此操作。

请注意,如果服务器和客户端位于同一台计算机上,Windows 服务器具有不使用 SSPI 的后备模式。当您从测试转向产品时,这会让您感到焦灼。

If you have a problem related to the SPN, please see this answer: SO WCF-Security-Problem question

The only issue you should have with the load balancer is whether you need your session to remain "sticky" to one host. For a given session the load balancer should be able to do this for you if you configure it correctly.

Note that windows server has a fallback mode that does not use SSPI if you have the server and client on the same machine. This lets you get burnt when you move from test to prod.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文