在对C#.NET 3.5服务器进行DCOM调用时,如何可靠地检查客户端身份?

我有一个旧的Win32 C ++ DCOM服务器,我正在重写使用C#.NET 3.5。 客户端应用程序位于远程Windows XP计算机上,也是用C ++编写的。 这些客户端必须保持不变,因此我必须在新的.NET对象上实现接口。

这已经完成,并且正在成功地实现接口,并且正确地从旧客户端到新.NET对象进行所有调用。

但是,我在从DCOM客户端获取调用用户的身份时遇到问题。 为了尝试识别发起DCOM呼叫的用户,我在服务器上有以下代码……

[DllImport("ole32.dll")] static extern int CoImpersonateClient(); [DllImport("ole32.dll")] static extern int CoRevertToSelf(); private string CallingUser { get { string sCallingUser = null; if (CoImpersonateClient() == 0) { WindowsPrincipal wp = System.Threading.Thread.CurrentPrincipal as WindowsPrincipal; if (wp != null) { WindowsIdentity wi = wp.Identity as WindowsIdentity; if (wi != null && !string.IsNullOrEmpty(wi.Name)) sCallingUser = wi.Name; } if (CoRevertToSelf() != 0) ReportWin32Error("CoRevertToSelf"); } else ReportWin32Error("CoImpersonateClient"); return sCallingUser; } } private static void ReportWin32Error(string sFailingCall) { Win32Exception ex = new Win32Exception(); Logger.Write("Call to " + sFailingCall + " FAILED: " + ex.Message); } 

当我获得CallingUser属性时,前几次返回的值是正确的,并且识别出正确的用户名,但是,在3或4个不同的用户成功拨打电话后(并且它有所不同,所以我不能更具体)进一步的用户似乎被识别为早先打过电话的用户。

我注意到的是前几个用户在他们自己的线程上处理他们的DCOM调用(也就是说,来自特定客户端的所有调用都由一个唯一的线程处理),然后后续用户由相同的线程处理较早的用户,在调用CoImpersonateClient()CurrentPrincipal与该线程的初始用户的匹配。

为了显示:

用户Tom进行由线程1处理的DCOM调用( CurrentPrincipal正确识别Tom)

用户Dick进行由线程2处理的DCOM调用( CurrentPrincipal正确识别Dick)

用户Harry进行由线程3处理的DCOM调用( CurrentPrincipal正确识别Harry)

用户Bob进行由线程3处理的DCOM调用( CurrentPrincipal错误地将他识别为Harry)

正如您在此图中所看到的,来自客户Harry和Bob的呼叫正在线程3上处理,并且服务器正在将呼叫客户端识别为Harry。

有什么我做错了吗? 以这种方式使用模拟是否有任何警告或限制? 有没有更好或不同的方式我可以可靠地实现我想做的事情?

非常感谢所有的帮助。

好的,所以我采取了不同的方法,并且finall提出了一种似乎有效的方法(针对8个不同的远程用户进行了测试)。

我放弃了Impersonation路线,转而使用ClientBlankets ……

 [DllImport("ole32.dll")] static extern int CoQueryClientBlanket(out IntPtr pAuthnSvc, out IntPtr pAuthzSvc, [MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pServerPrincName, out IntPtr pAuthnLevel, out IntPtr pImpLevel, out IntPtr pPrivs, out IntPtr pCapabilities); public static string CallingUser { get { IntPtr pAthnSvc = new IntPtr(); IntPtr pAthzSvc = new IntPtr(); StringBuilder pServerPrincName = new StringBuilder(); IntPtr pAuthnLevel = new IntPtr(); IntPtr pImpLevel = new IntPtr(); IntPtr pPrivs = new IntPtr(); IntPtr pCaps = new IntPtr(4); string sCallingUser = string.Empty; try { CoQueryClientBlanket(out pAthnSvc, out pAthzSvc, out pServerPrincName, out pAuthnLevel, out pImpLevel, out pPrivs, out pCaps); } catch (Exception ex) { Logger.Write(ex.Message); } finally { sCallingUser = System.Runtime.InteropServices.Marshal.PtrToStringAuto(pPrivs); } return sCallingUser; } } 

使用CoCreateClientBlanket似乎具有所需的结果,并且无论使用哪个线程来处理消息,我都能够可靠地每次都获得调用用户的身份。