Thread.CurrentPrincipal不会在WCF客户端回调中保留

时间:2014-10-23 16:52:40

标签: c# multithreading wcf wcf-security wcf-callbacks

我在netTcpBinding上有一个双工WCF服务,该服务在客户端使用订阅模型 将使用Subscribe / Unsubscribe方法通过回调实现订阅服务。

该服务具有自定义IAuthorizationPolicy,用于对数据库中的用户进行身份验证 在IAuthorizationPolicy.Evaluate()方法中将默认线程主体设置为自定义主体。

该服务在IIS 7.5中以LOCALSYSTEM标识,Windows 7

运行

首先,客户端(WinForms)如何对用户进行身份验证:

  1. 显示登录表单以索取凭据
  2. 在另一个端点上调用另一个身份验证服务 验证用户凭据
  3. 使用AppDomain.SetThreadPrincipal
  4. 设置默认线程主体

    我面临的问题是,在回调线程中,并且只在回调线程中,将Thread.CurrentPrincipal重置为匿名GenericPrincipal对象,并且我的自定义主体在身份验证后未传播。


    UPDATE:如果我将securityMode和clientCredentialType设置为“None”,则会按预期传播主体,显然这与安全配置有关,我尝试了很多配置组合而没有运气


    更新:我添加了sample project来重现此问题。

    解决方案是直接的,所有配置和运行它只需几次点击和几分钟的宝贵时间,它包含5个项目:

    1. PM.Services - WCF服务项目
    2. PM.Contracts - 包含服务合同的lib
    3. Client / Broadcaster - 控制台应用程序通过该服务向其他客户端广播。
    4. 客户端/侦听器 - 订阅服务/侦听广播的控制台应用程序 (这是重置Thread.CurrentPrincipal的位置)
    5. 客户端/共享 - 包含回调实现和服务创建代码的两个客户端之间共享的lib。
    6. 要在您的计算机上配置服务(假设已在计算机上启用IIS7或更高版本),解决方案根文件夹包含两个批处理文件:

      InstallService.bat:此批次将:

      • 在您的计算机上启用以下Windows功能及其父功能(如果尚未启用): Windows Communication Foundation HTTP激活 Windows 通信基础非HTTP激活,以启用WCF和 的net.tcp。

      • 将在TrustedPeople商店中创建和添加服务证书。

      • 将为将要收听的服务创建一个网站:7359 (http)和7357(net.tcp)

      • 将在LOCALSYSTEM身份下创建一个ApplicationPool(以避免 测试期间的许可问题)

      • 将在c:\ inetpub \ WCFCallbackSample中创建一个文件夹并将服务输出复制到该文件夹​​。

      UninstallService.bat :此批次将:

      • 从TrustedPeople商店中删除服务证书。

      • 卸载服务网站。

      • 删除网站文件夹。

      运行示例解决方案:

      • 构建解决方案

      • 使用InstallService.bat安装服务。

      • 调试/运行解决方案以运行两个服务使用者 (Client \ Broadcaster,Client \ Listener)

      • 从Broadcaster发送消息

      • 在监听器中,您将注意到回调线程标识 (打印在控制台上)与主线程的标识不同 在应用程序启动时由SetThreadPrincipal设置。


      初始帖子的代码:

      服务界面:

      [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IFinanceServiceCallback))]
      public interface IFinanceService : IServiceContract, ISupportCallback // <-- Interface that defines Subscribe and Unsubscribe methods
      {
          [OperationContract]
          void DuplexMessageTest(string text);
      }
      

      服务实施:

      [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)] 
      public class FinanceService : IFinanceService
      {
          // Subscription boilerplate code
          private static readonly 
              DuplexServiceSubscriptionManager<IFinanceServiceCallback> _subscriptionManager = new DuplexServiceSubscriptionManager<IFinanceServiceCallback>();
      
          public bool Subscribe()
          {
              return _subscriptionManager.Subscribe();
          }
      
          public bool Unsubscribe()
          {
              return _subscriptionManager.Unsubscribe();
          }
      
          // A test method for simulation
          public void DuplexMessageTest(string text)
          {
              _subscriptionManager.InvokeCallbackMethod(callback => callback.OnDuplexTest(text));
          }
      

      回调实施:

      [CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Single,  AutomaticSessionShutdown=true)]
      public class FinanceServiceCallbackSubscription :  BaseServiceSubscription<IFinanceService>, IFinanceServiceCallback
      {
          public FinanceServiceCallbackSubscription() : base()
          {
          }
      
          public delegate void DuplexTestEventHandler(string message);
      
          public event DuplexTestEventHandler DuplexTest;
      
          public void OnDuplexTest(string message)
          {
              -->> At this point, the Thread.CurrentPrincipal is reset <<--
              if (DuplexTest != null)
                  DuplexTest(message);
          }
      }
      

      更新服务配置

      <system.serviceModel>
          <services>
          ...
          <service behaviorConfiguration="PM.Behaviour_01" name="PM.Services.FinanceService">
              <endpoint address="" binding="netTcpBinding" bindingConfiguration="PMStandardBindingConfiguration"
                  contract="PM.Contracts.IFinanceService" />
              <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
          </service>
          ...
          <services/>
          <bindings>
              ...
              <netTcpBinding>
                  <binding name="PMStandardBindingConfiguration"
                          closeTimeout="00:01:00"
                          openTimeout="00:01:00"
                          receiveTimeout="00:10:00"
                          sendTimeout="00:10:00"
                          maxReceivedMessageSize="2147483647"
                          maxBufferPoolSize="0"
                          transferMode="Buffered"
                          portSharingEnabled="false">
      
                    <readerQuotas maxDepth="2147483647"
                                  maxStringContentLength="2147483647"
                                  maxArrayLength="2147483647"
                                  maxBytesPerRead="2147483647"
                                  maxNameTableCharCount="16384" />
                    <security mode="Message">
                      <message clientCredentialType="UserName" />
                    </security>
                    <reliableSession ordered="true" inactivityTimeout="10675199.02:48:05.4775807" />
                  </binding>
              </netTcpBinding>
              ...
          <bindings/>
          <behaviors>
              ...
              <behavior name="PM.Behaviour_01">
                <serviceMetadata httpGetEnabled="false" />
                <serviceCredentials>
                  <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="PM.Services.Security.UserValidator, PM.Services" />
                  <serviceCertificate findValue="PMSV" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName" />
                  <clientCertificate>
                    <authentication certificateValidationMode="PeerTrust" />
                  </clientCertificate>
                </serviceCredentials>
      
                <serviceAuthorization principalPermissionMode="Custom">
                  <authorizationPolicies>
                    <add policyType='PM.Services.Security.PMAuthorizationPolicy, PM.Services' />
                  </authorizationPolicies>
                </serviceAuthorization>
                <ErrorHandler /> <!--Error handling behaviour-->
                <serviceDebug includeExceptionDetailInFaults="false" />
              </behavior>
              ...
          <behaviors/>
      <system.serviceModel/>
      

      更新工厂方法我用以下方法创建服务:

      public static ServiceClient<TServiceContract> CreateService<TServiceContract>(object callbackInstance, string username, string password) 
          where TServiceContract: IServiceContract
      {
          Binding binding = STANDARD_BINDING;
          bool supplyCredentials = true;
          Type callbackImplementationType = null;
          string protocol = "http";
          string addr = "";
          int port = PM_STANDARD_HTTP_PORT;
      
          if (BINDING_MAPPING.ContainsKey(typeof(TServiceContract)))
          {
              var args = BINDING_MAPPING[typeof(TServiceContract)];
              binding = (Binding)args[0];
              protocol = (string)args[1];
              object callbackType = args[2];
              if (callbackType != null)
                  callbackImplementationType = (Type)callbackType;
              supplyCredentials = (bool)args[3];
          }
      
          // Convention: Service .svc file in url is the same as contract 
          // without the leading "I"
          // e.g. IFinanceService becomes FinanceService
          // so url becomes 
          // http://localhost/PMServices/FinanceService.svc
      
          string serviceName = typeof(TServiceContract).Name.Substring(1); // Remove leading I from interface name
          string baseUri = Settings.GetString(iSetting.ServiceBaseURI);
      
          UriBuilder b = new UriBuilder(baseUri);
          if (protocol == "net.tcp")
              port = PM_STANDARD_TCP_PORT;
      
          addr = string.Format("{0}://{1}:{2}/{3}/{4}.svc",
              protocol,
              b.Host,
              port,
              b.Path.Replace("/", ""),
              serviceName);
      
          EndpointIdentity identity = 
              EndpointIdentity.CreateDnsIdentity("PMSV");
          EndpointAddress endpointAddress =
              new EndpointAddress(new Uri(addr), identity);
      
          ChannelFactory<TServiceContract> channelFactory = null;
      
          // Check whether service is duplex
          if((binding.GetType() == typeof(NetTcpBinding) || binding.GetType() == typeof(WSDualHttpBinding))
              && (callbackImplementationType != null || callbackInstance != null))
          {
              object callbackImplementation = callbackInstance;
              if(callbackImplementation == null && callbackImplementationType != null)
                  callbackImplementation = Activator.CreateInstance(callbackImplementationType, null);
      
              if (callbackImplementation != null)
                  channelFactory = new DuplexChannelFactory<TServiceContract>(
                      callbackImplementation,
                      binding, 
                      endpointAddress);
              else // Create non-duplex channel if no callback implementation is specified
                  channelFactory = new ChannelFactory<TServiceContract>(binding,
                      endpointAddress);
          }
          else
              channelFactory = new ChannelFactory<TServiceContract>(binding, 
                  endpointAddress);
      
          if (supplyCredentials)
          {
              channelFactory.Credentials.UserName.UserName = 
                  username ?? PMClientPrincipal.Current.User.Name;
              channelFactory.Credentials.UserName.Password = 
                  password ?? ((PMClientIdentity)PMClientPrincipal
                  .Current
                  .User).Password;
          }
      
          return new ServiceClient<TServiceContract>(channelFactory,
              channelFactory.CreateChannel());
      }
      

      PMClientPrincipal是我对IPrincipal的实现, PMClientPrincipal.Current 是一个静态属性,只是将当前线程主体转换为PMClientPrincipal

      UPDATE 管理客户订阅的类(在服务中使用)

      public class DuplexServiceSubscriptionManager<TCallbackImplementation>
          {
              public DuplexServiceSubscriptionManager()
              {
                  _subscribers = new List<TCallbackImplementation>();
              }
      
              private object _syncRoot = new object();
              public List<TCallbackImplementation> _subscribers;
      
              private void AddClient(TCallbackImplementation callback)
              {
                  lock (_syncRoot)
                  {
                      if (!_subscribers.Contains(callback))
                      {
                          _subscribers.Add(callback);
                          ((ICommunicationObject)callback).Closed += OnClientLost;
                          ((ICommunicationObject)callback).Faulted += OnClientLost;
                      }
                  }
              }
      
              private void RemoveClient(TCallbackImplementation callback)
              {
                  lock (_syncRoot)
                  {
                      if (_subscribers.Contains(callback))
                      {
                          _subscribers.Remove(callback);
                          ((ICommunicationObject)callback).Closed -= OnClientLost;
                          ((ICommunicationObject)callback).Faulted -= OnClientLost;
                      }
                  }
              }
      
              void OnClientLost(object sender, EventArgs e)
              {
                  ServiceLogger.Log.DebugFormat("Client lost, reason: {0}", ((ICommunicationObject)sender).State);
                  if (OperationContext.Current == null)
                      return;
      
                  TCallbackImplementation callback = OperationContext.Current.GetCallbackChannel<TCallbackImplementation>();
                  RemoveClient(callback);
              }
      
              public bool Subscribe()
              {
                  try
                  {
                      TCallbackImplementation callback = OperationContext.Current.GetCallbackChannel<TCallbackImplementation>();
                      AddClient(callback);
                      ServiceLogger.Log.Debug("Client subscribed.");
                      return true;
                  }
                  catch
                  {
                      return false;
                  }
              }
      
              public bool Unsubscribe()
              {
                  try
                  {
                      TCallbackImplementation callback = OperationContext.Current.GetCallbackChannel<TCallbackImplementation>();
                      RemoveClient(callback);
                      ServiceLogger.Log.Debug("Client unsubscribed.");
                      return true;
                  }
                  catch
                  {
                      return false;
                  }
              }
      
              public void InvokeCallbackMethod(Action<TCallbackImplementation> target)
              {
                  _subscribers.ForEach(delegate(TCallbackImplementation callback)
                  {
                      if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                      {
                          try
                          {
                              target.Invoke(callback);
                          }
                          catch (Exception)
                          {
                              Unsubscribe();
                          }
                      }
                      else
                      {
                          Unsubscribe();
                      }
                  });
              }
          }
      

      UPDATE 调用堆栈到回调方法

      System.ServiceModel.dll!System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(object instance, object[] inputs, out object[] outputs) Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(ref System.ServiceModel.Dispatcher.MessageRpc rpc) Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(ref System.ServiceModel.Dispatcher.MessageRpc rpc) Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(ref System.ServiceModel.Dispatcher.MessageRpc rpc) Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref System.ServiceModel.Dispatcher.MessageRpc rpc)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool isOperationContextSet)   Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(ref System.ServiceModel.Dispatcher.MessageRpc rpc, bool isOperationContextSet) Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext request, bool cleanThread, System.ServiceModel.OperationContext currentOperationContext)   Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext request, System.ServiceModel.OperationContext currentOperationContext)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult result)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnAsyncReceiveComplete(System.IAsyncResult result)    Unknown
      System.ServiceModel.Internals.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.AsyncQueueReader.Set(System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.Item item)   Unknown
      System.ServiceModel.Internals.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.EnqueueAndDispatch(System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.Item item, bool canDispatchOnThisThread)   Unknown
      System.ServiceModel.Internals.dll!System.Runtime.InputQueue<System.ServiceModel.Channels.Message>.EnqueueAndDispatch(System.ServiceModel.Channels.Message item, System.Action dequeuedCallback, bool canDispatchOnThisThread)   Unknown
      System.ServiceModel.Internals.dll!System.Runtime.InputQueue<System.__Canon>.EnqueueAndDispatch(System.__Canon item) Unknown
      System.ServiceModel.dll!System.ServiceModel.Security.SecuritySessionClientSettings<System.ServiceModel.Channels.IDuplexSessionChannel>.ClientSecurityDuplexSessionChannel.CompleteReceive(System.IAsyncResult result)   Unknown
      System.ServiceModel.dll!System.ServiceModel.Security.SecuritySessionClientSettings<System.ServiceModel.Channels.IDuplexSessionChannel>.ClientSecurityDuplexSessionChannel.OnReceive(System.IAsyncResult result) Unknown
      System.ServiceModel.Internals.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously, System.Exception exception)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Security.SecuritySessionClientSettings<System.ServiceModel.Channels.IDuplexSessionChannel>.ClientSecuritySessionChannel.ReceiveAsyncResult.OnReceive(System.IAsyncResult result)    Unknown
      System.ServiceModel.Internals.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously, System.Exception exception)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.ReliableChannelBinder<System.ServiceModel.Channels.IDuplexSessionChannel>.InputAsyncResult<System.ServiceModel.Channels.ReliableChannelBinder<System.ServiceModel.Channels.IDuplexSessionChannel>>.OnInputComplete(System.IAsyncResult result) Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.ReliableChannelBinder<System.ServiceModel.Channels.IDuplexSessionChannel>.InputAsyncResult<System.ServiceModel.Channels.ReliableChannelBinder<System.ServiceModel.Channels.IDuplexSessionChannel>>.OnInputCompleteStatic(System.IAsyncResult result)   Unknown
      System.ServiceModel.Internals.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously, System.Exception exception)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.TransportDuplexSessionChannel.TryReceiveAsyncResult.OnReceive(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.Fx.AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult result)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously)  Unknown
      System.ServiceModel.Internals.dll!System.Runtime.AsyncResult.Complete(bool completedSynchronously, System.Exception exception)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SynchronizedMessageSource.SynchronizedAsyncResult<System.ServiceModel.Channels.Message>.CompleteWithUnlock(bool synchronous, System.Exception exception)   Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SynchronizedMessageSource.ReceiveAsyncResult.OnReceiveComplete(object state)   Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SessionConnectionReader.OnAsyncReadComplete(object state)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SocketConnection.FinishRead()  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SocketConnection.OnReceiveAsync(object sender, System.Net.Sockets.SocketAsyncEventArgs eventArgs)  Unknown
      System.ServiceModel.dll!System.ServiceModel.Channels.SocketConnection.OnReceiveAsyncCompleted(object sender, System.Net.Sockets.SocketAsyncEventArgs e) Unknown
      System.dll!System.Net.Sockets.SocketAsyncEventArgs.OnCompleted(System.Net.Sockets.SocketAsyncEventArgs e)   Unknown
      System.dll!System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(System.Net.Sockets.SocketError socketError, int bytesTransferred, System.Net.Sockets.SocketFlags flags)   Unknown
      System.dll!System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped)   Unknown
      mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) Unknown
      

0 个答案:

没有答案