【问题标题】:Renewing a WCF client when SCT has expired?SCT 过期时续订 WCF 客户端?
【发布时间】:2010-10-03 12:54:20
【问题描述】:

我正在使用 WCF 到使用安全模式“TransportWithMessageCredential”的肥皂端点。

WCF 客户端/服务器使用 SCT(安全上下文令牌)来维护安全连接,并且在一般情况下按预期工作。

但是,在一段时间不活动后,SCT 将过期,下一次方法调用将引发 MessageSecurityException:

从另一方收到不安全或不正确安全的故障。故障代码及详情见内部FaultException

内部异常:

无法处理该消息。这很可能是因为操作“http://tempuri.org/IMyService/MyMethod”不正确,或者因为消息包含无效或过期的安全上下文令牌,或者因为绑定之间存在不匹配。如果服务因不活动而中止通道,则安全上下文令牌将无效。为防止服务过早中止空闲会话,请增加服务端点绑定的接收超时。

在后续调用中,当我看到 CommunicationState 出现故障时,我会更新连接。但是我无法找到一种方法来在调用我的方法之前抢先检查 SCT 是否已过期。

【问题讨论】:

标签: .net wcf performance


【解决方案1】:

在第一个答案的基础上,我想出了这个解决方案,它通常包装了由 svcutil.exe 创建的自动生成的客户端代理:

public class ProxyWrapper<T> where T : ICommunicationObject
{
    private T _service;

    public ProxyWrapper()
    {
        _service = CreateNewInstance();
    }

    public void Invoke(Action<T> action)
    {
        try
        {
            action(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            action(_service);
        }
    }

    public TResult Invoke<TResult>(Func<T, TResult> func)
    {
        try
        {
            return func(_service);
        }
        catch (MessageSecurityException)
        {
            if (_service.State != CommunicationState.Faulted)
            {
                throw;
            }

            _service.Abort();
            _service = CreateNewInstance();

            return func(_service);
        }
    }

    private T CreateNewInstance()
    {
        Type type = typeof(T);
        return (T)type.GetConstructor(Type.EmptyTypes).Invoke(null);
    }
}

要使用它,您需要做的就是:

ProxyWrapper<ServiceClient> client = new ProxyWrapper<ServiceClient>();
client.Invoke(s => s.SomeAction());
int val = client.Invoke<int>(s => s.ReturnsAnInteger());

注意:因为我只使用客户端代理的默认构造函数,这就是所有这些支持。

【讨论】:

    【解决方案2】:

    查看这篇文章,您可以在其中下载代理包装器,该包装器会在会话到期时重试。

    http://www.dasblonde.net/2008/04/24/MyProxyWrapperAndTheEVILSUOFile.aspx

    【讨论】:

    【解决方案3】:

    您也许可以使用装饰器模式来处理带有 WCF 代理的异常。如果这条路径对您开放,您可以考虑这样的设置,它将处理代理故障并为调用者重新初始化它。随后的异常将被抛出给调用者。

    //Proxy implements this
    interface IMyService
    {
    
      void MyMethod();
    
    }
    
    //Decorator 1
    public class MyServiceProxyRetryingDecorator : IMyService
    {
    
      //This is the real proxy that might be faulted
      private realProxy = new RealProxy();
    
      public void MyMethod()
      {
        ReEstablishProxyIfNecessary();
        //now just pass the call to the proxy, if it errors again, 
        //do more handling or let the exception bubble up
        realProxy.MyMethod();
      }
    
      private void ReEstablishProxyIfNecessary()
      {
        if(realProxy.CommunicationState == CommunicationState.Faulted)
        {
           realProxy.Abort();
           realProxy = new RealProxy();
        }
      }
    }
    

    装饰器的不同版本可以让装饰器处理您的 MessageSecurityException,并在捕获时重新初始化真正的代理:

    //Decorator 2
    public class MyServiceProxyExceptionHandlingDecorator : IMyService
    {
    
      //This is the real proxy that might be faulted
      private realProxy = new RealProxy();
    
      public void MyMethod()
      {
        try {realProxy.MyMethod(); } 
        catch (ExceptionYouAreInterestedIn ex)
        { 
        ReEstablishProxyIfNecessary(); 
        realProxy.MyMethod(); //do it again
        }
      }
    
      private void ReEstablishProxyIfNecessary()
      {
        if(realProxy.CommunicationState == CommunicationState.Faulted)
        {
           realProxy.Abort();
           realProxy = new RealProxy();
        }
      }
    }
    

    【讨论】:

    • 您的模式不起作用。整个问题是“realProxy”仅在调用 MyMethod 之后才处于故障状态,而不是之前。问题是如何提前发现代理是否会失败,因为客户端显然必须知道它的 SCT 到期情况。
    • 我不确定您是否可以在尝试调用某个方法之前发现该方法会出现 SCT 错误。我已经修改了我的帖子,以展示如何为客户处理异常,这样他们就不必这样做了。
    【解决方案4】:

    您可以使用委托来解决您的问题。这将允许安全地调用该操作,如果失败,则捕获异常,构造一个新的服务实例并再次执行该操作。

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Security;
    
    public static class Service
    {
        private static IService _service;
    
        public static void Invoke(Action<IService> action)
        {
            try
            {
                action(_service);
            }
            catch (MessageSecurityException)
            {
                if (_service.State != CommunicationState.Faulted)
                {
                    throw;
                }
    
                _service.Abort();
                _service = CreateFreshInstance();
    
                action(_service);
            }           
        }
    }
    

    然后您可以像Service.Invoke(s =&gt; s.Method()); 这样调用您的帮助程序类来调用 IService.Method()。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 2010-12-04
      • 1970-01-01
      • 2023-03-31
      相关资源
      最近更新 更多