【问题标题】:Invalid or expired security context token in WCF web serviceWCF Web 服务中的安全上下文令牌无效或过期
【发布时间】:2010-10-29 02:16:06
【问题描述】:

全部,

我有一个使用服务帐户(VM,Windows 2003 SP2)在 IIS 下托管的 WCF Web 服务(我们称之为服务“B”)。该服务公开了一个使用默认值的 WSHttpBinding 的端点,除了 maxReceivedMessageSize、maxBufferPoolSize、maxBufferSize 和一些已增加的超时。

Web 服务已使用 Visual Studio 负载测试框架对大约 800 个并发用户进行了负载测试,并且成功通过了所有测试,没有引发异常。单元测试中的代理已从配置中创建。

有一个 sharepoint 应用程序使用 Office Sharepoint Server 搜索服务来调用 Web 服务“A”和“B”。应用程序将从服务“A”获取数据以创建将发送到服务“B”的请求。来自服务“B”的响应被编入索引以供搜索。代理是使用 ChannelFactory 以编程方式创建的。

当服务“A”花费不到 10 分钟时,对服务“B”的调用成功。但是,当服务“A”需要更多时间(约 20 分钟)时,对服务“B”的调用会引发以下异常:

异常消息:从另一方收到不安全或不正确安全的故障。故障代码和细节见内部FaultException 内部异常消息:无法处理该消息。这很可能是因为操作 'namespace/OperationName' 不正确,或者因为消息包含无效或过期的安全上下文令牌,或者因为绑定之间存在不匹配。如果服务由于不活动而中止通道,则安全上下文令牌将无效。为防止服务过早中止空闲会话,请增加服务端点绑定的接收超时。

绑定设置相同,客户端服务器和Web服务服务器的时间与Windows时间服务同步,时区相同。

当我查看托管 Web 服务“B”的服务器时,我可以看到正在记录以下安全错误:

来源:安全

类别:登录/注销

事件 ID:537

用户 NT AUTHORITY\SYSTEM

登录失败:

原因:登录时出错

登录类型:3

登录过程:Kerberos

身份验证包:Kerberos

状态码:0xC000006D

子状态码:0xC0000133

在线阅读一些博客后,状态码表示STATUS_LOGON_FAILURE,子状态码表示STATUS_TIME_DIFFERENCE_AT_DC。但我已经检查了服务器和客户端时钟,并且它们已同步。

我还注意到安全令牌似乎缓存在客户端服务器的某个位置,因为它们有另一个进程使用相同的服务帐户调用 Web 服务“B”并在第一次调用时成功获取数据。然后他们开始更新办公室共享点服务器搜索服务索引的过程,但它失败了。然后,如果他们再次调用第一个进程,它也会失败。

有没有人遇到过此类问题或有什么想法?

问候,

--达米安

【问题讨论】:

    标签: c# wcf iis active-directory wcf-security


    【解决方案1】:

    10 分钟是默认接收超时。如果您的代理闲置超过 10 分钟,则该代理的安全会话将被服务器中止。启用日志记录,您将在服务器的诊断日志中看到这一点。您报告的错误消息适合此行为。 在系统诊断文件中搜索“SessionIdleManager”。如果你找到了,以上就是你的问题了。

    试一试,为客户端和服务器设置建立安全上下文="false"。

    【讨论】:

      【解决方案2】:

      不要在 using 语句中调用服务操作。而是使用诸如...的模式

      client = new ServiceClient("Ws<binding>")
      try
      {
          client.Operation(x,y);
          client.Close();
      }
      catch ()
      {
          client.Abort();
      }
      

      我不明白为什么会这样,但我猜当代理超出 using 语句的范围时,不会调用 Close。然后服务会等待直到 receiveTimeout(在绑定上)过期,然后中止连接,导致后续调用失败。

      【讨论】:

      • 我停止使用使用声明进行服务。如果我需要处理它,请调用显式 ((IDispose)Service).Dispose();终于
      • using语句的问题是,如果在调用过程中出现异常,using语句会在客户端调用Dispose(),客户端会调用底层通道的Close()。由于连接已经出现故障,这将引发另一个异常,因此第一个异常将永远不会出现。
      【解决方案3】:

      我认为这里发生的事情是您的频道正在超时(正如您所怀疑的那样)。

      如果我理解正确,超时的不是对服务 A 的调用,而是对服务 B 的调用,在您之前打电话给你。

      我猜您是在调用服务A之前创建频道,而不是及时(即在调用服务之前) B)。您应该在使用它之前创建通道(代理、服务客户端),例如:

      AResponse aResp = null;
      BResponse bResp = null;
      using (ServiceAProxy proxyA = new ServiceAProxy())
      {
         aResp = proxyA.DoServiceAWork();
         using (ServiceBProxy proxyB = new ServiceBProxy())
         {
            bResp = proxyB.DoOtherork(aResp);
         }
      }
      return bResp;
      

      不过,我相信,一旦您解决了那个问题(服务 B 超时),您就会意识到 sharepoint 应用的代理(称为服务 A)将超时。 为了解决这个问题,您可能希望将服务模型从请求-响应更改为发布-订阅模型。

      对于长期运行的服务,您会希望您的 sharepoint 应用订阅服务 A,并让服务 A 在准备就绪时发布其结果 - 无论需要多长时间。

      Juval Lowey 编写的Programming WCF Services (O'Reilly) 有很好的解释,IDesign(Juval 的公司)发布了a great set of coding standards for WCF,以及一个很棒的Publish-Subscribe Framework 的代码。

      希望这会有所帮助, 阿萨夫。

      【讨论】:

        【解决方案4】:

        我刚才实际上是通过做一些愚蠢的事情触发了这个错误。我有一个修改系统日期的单元测试,以测试一些基于时间的功能。而且我猜我创建上下文和调用我的方法之间的明显时间差(由于系统日期的更改)导致某些内容过期。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-15
          • 2020-05-24
          • 2013-05-10
          • 1970-01-01
          • 2017-09-24
          • 2016-05-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多