使用Kerberos身份验证的WCF:无法满足安全令牌请求,因为身份验证失败

时间:2015-03-05 21:12:50

标签: c# web-services wcf authentication kerberos

我正在尝试从客户端应用程序(Windows窗体)执行Kerberos身份验证到在IIS下运行的一台Windows 2008 R2服务器上的WCF Web服务,该服务器又调用另一台运行在另一台Windows 2008 R2服务器上的WCF服务在IIS下。我已经看到这称为Kerberos双跳认证。

当我在同一台Windows 2008 R2服务器上找到这两个Web服务时,我们的双跃点身份验证工作正常。但是,当我们将第二个WCF服务移动到另一个服务器时,两个Web服务之间的身份验证失败。我不知道导致这个问题的原因可能是配置问题,或者是我们的服务器/网络设置中的问题。客户端和服务器都存在于同一个域中。

以下是我到目前为止所做的更多细节。我已经尝试过关于这个问题的其他相关主题/问题的许多建议,但到目前为止没有任何喜悦。

  • 我创建了一个连接到a的Windows窗体客户端应用程序 WCF Web服务(我将其称为中间服务),以及 从登录用户名返回登录用户的用户名 服务。
  • 在客户端app.config(见下文)中我们指定了'Windows' 身份验证(请参阅下面的配置)。
  • 中间服务在我们的服务器'SERVER1'上运行。
  • 中间服务将用户名返回到客户端应用程序, 因此,中间服务身份验证必须正常工作。
  • 我在Middle服务中有另一个调用另一个WCF的方法 Web服务(我将此第二项服务称为End服务), 并且再次从绑定指定Windows身份验证 中端服务到终端服务。如果我找到了End服务 与中间服务相同的服务器然后再次认证工作 预期,登录用户的用户名(即:我)是 通过Middle返回End服务的客户端应用程序 服务。但是,如果我在另一台服务器上找到End服务 (SERVER2)认证(双跳)失败,'内部异常: 无法满足安全令牌的请求,因为 身份验证失败。'
  • 客户端应用,中间服务和终端服务都存在于同一个中 域名(MYDOMAIN)。
  • 我正在使用服务帐户(MYDOMAIN \ MY-HOST_ACCOUNT)来运行这两个帐户 中端服务,并创建服务主体名称(SPN) 这称为'HTTP / SERVER1.int.mydomain.com'。我也信任 此服务帐户,以及Kerberos的计算机/计算机“SERVER1” 在Active Directory中委派(信任此用户/计算机) 授权任何服务)。 Bindings使用'Message'安全模式 这是在Web服务绑定中指定的。
  • 我已经包含了客户端app.config和两个Web服务 web.configs(请参见下文)。
  • 我尝试了各种不同的配置设置,以及这些设置 当服务位于同一个服务器上时,当前设置工作正常 服务器,但服务在不同时发生错误 服务器。我试过'真'和'假' 'negotiateServiceCredential',但相信这需要'真实'。一世 当此设置为false时,会收到不同的错误消息 使用Kerberos的身份验证模式不支持模拟 级别'委托'。指定标识或模仿)。

希望之前处理过“双跳”WCF Kerberos身份验证的人可能会认识到这个问题,并能够帮助我。

非常感谢

客户端APP配置

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IMiddleService">
                    <security mode="Message">
                        <message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <behaviors>
          <endpointBehaviors>
            <behavior name="DelegationBehavior">
              <clientCredentials>
                <windows allowedImpersonationLevel="Delegation" />
              </clientCredentials>
            </behavior>
          </endpointBehaviors>
        </behaviors>
        <client>
          <endpoint address="http://SERVER1/KerberosMiddleService/MiddleService.svc"
              behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding"
              bindingConfiguration="WSHttpBinding_IMiddleService" contract="KerberosMiddleService.IMiddleService"
              name="WSHttpBinding_IMiddleService">
            <identity>
              <servicePrincipalName value="HTTP/SERVER1.int.mydomain.com"/>
              <userPrincipalName value="MYDOMAIN\MY-HOST_ACCOUNT@int.mydomain.com"/>
            </identity>
          </endpoint>
        </client>
    </system.serviceModel>
</configuration>

MIDDLE SERVICE WEB CONFIG

<?xml version="1.0"?>
<configuration>
  <appSettings/>
  <system.web>
    <compilation targetFramework="4.0"/>
    <httpRuntime/>
    <customErrors mode="Off"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IEndService">
          <security mode="Message">
            <message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default"/>
          </security>
         </binding>
          <binding name="WSHttpBinding_IEndService1">
          <security mode="Message">
            <message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" algorithmSuite="Default" />
          </security>
         </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceAuthorization impersonateCallerForAllOperations="true"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="DelegationBehavior">
         <clientCredentials>
           <windows allowedImpersonationLevel="Delegation" />
         </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint address="http://SERVER2/endservice/endservice.svc" 
                behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding" 
                bindingConfiguration="WSHttpBinding_IEndService" contract="KerberosEndService.IEndService"
                name="WSHttpBinding_IEndService">
      </endpoint>
      <endpoint address="http://SERVER1/kerberosendservice/endservice.svc" 
                behaviorConfiguration="DelegationBehavior" binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_IEndService1" contract="BT01_KerberosEndService.IEndService"
                name="WSHttpBinding_IEndService1">
      </endpoint>
    </client>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme="http"/>
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

END SERVICE WEB CONFIG

<?xml version="1.0"?>
<configuration>
  <appSettings/>
<system.web>
    <customErrors mode="Off"/>
    <compilation targetFramework="4.0"/>
    <httpRuntime/>
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceAuthorization impersonateCallerForAllOperations="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="wsHttpBinding" scheme="http"/>
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

WCF堆栈痕迹的一部分

<ExceptionString>System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---&amp;gt; System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
   at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
   --- End of inner exception stack trace ---</ExceptionString><InnerException><ExceptionType>System.ServiceModel.FaultException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The request for security token could not be satisfied because authentication failed.</Message><StackTrace>   at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)</StackTrace><ExceptionString>System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
   at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)</ExceptionString></InnerException></Exception></TraceRecord></DataItem></TraceData></ApplicationData></E2ETraceEvent><E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent"><System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system"><EventID>131075</EventID><Type>3</Type><SubType Name="Error">0</SubType><Level>2</Level><TimeCreated SystemTime="2015-03-02T21:04:14.4059347Z" /><Source Name="System.ServiceModel" /><Correlation ActivityID="{acfc80d6-b119-4f57-aaf2-65f1319b9fca}" /><Execution ProcessName="w3wp" ProcessID="1432" ThreadID="43" /><Channel/><Computer>SERVER1</Computer></System><ApplicationData><TraceData><DataItem><TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error"><TraceIdentifier>http://msdn.microsoft.com/en-NZ/library/System.ServiceModel.Diagnostics.ThrowingException.aspx</TraceIdentifier><Description>Throwing an exception.</Description><AppDomain>/LM/W3SVC/1/ROOT/KerberosMiddleService-6-130698038017642990</AppDomain><Exception><ExceptionType>System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The caller was not authenticated by the service.</Message><StackTrace>   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetNextOutgoingMessage(Message incomingMessage, T negotiationState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)</StackTrace><ExceptionString>System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service. ---&amp;gt; System.ServiceModel.FaultException: The request for security token could not be satisfied because authentication failed.
   at System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)

1 个答案:

答案 0 :(得分:1)

好的,您还需要为终端服务添加另一个SPN:

HTTP/SERVER2.int.mydomain.com MYDOMAIN\MY-HOST_ACCOUNT)
HTTP/SERVER2 MYDOMAIN\MY-HOST_ACCOUNT)

最好同时指定FQDN和Netbios名称。确保您没有重复的SPN,否则Kerberos身份验证将无法正常工作。将SPN作为委派目标添加到域帐户(由于您对两个服务器使用相同的域帐户,因此可能不需要这样做。)

由于MiddleService需要模拟/委托给EndService,因此您需要使用本地安全策略为本地帐户授予权限 - 本地策略 - 用户权限分配:

作为操作系统的一部分

此用户权限允许进程在未经身份验证的情况下模拟任何用户。因此,该过程可以访问与该用户相同的本地资源。

认证后模拟客户

将此权限分配给用户允许代表该用户运行的程序模拟客户端。

请记住更改IIS应用程序设置以使用应用程序池凭据,以便使用域帐户进行身份验证。