在Asp.net开发的应用程序中,会有以windows集成认证的用户登录方式。会有这样的应用场景:
用户在本地上传一个Excel文件,其中包含了一些在本地编辑好的业务数据,需要上传到服务器更新到数据库中。作为服务端代码需要作很多的检查,以确保用户编辑的数据是有效的,这就需要较长时间的操作,不可能让用户在客户端(如IE)前一直等待返回。这就需要用委托的异步调用或多线程来处理上传文件后的操作。
因为新开辟的线程不同于主线程,它是以运行Asp.net的进程的user来启动的。(XP下的asp.net的进程是aspnet_wp.exe,对应的user是ASPNET,而在Windows server2003是w3wp.exe,对应的user是NETWORK SERVICE)
在新线程中免不了要访问数据库等需要身份验证的操作,以Asp.net的进程的user来验证显然是不正确的,这就需要把主线程的身份传到新线程中,在新线程中进行模拟。好在.Net Framework为我们提供了很简便的方法,下面看一下实例:
public class Test
{
private System.Security.Principal.WindowsIdentity curIndentity;
public void LongTimeHandle(string confirmFile)
{
WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(curIndentity.Token);
try
{
//Your code
string userName = Environment.UserName;
}
catch (Exception ex)
{
}
finally
{
impersonatedUser.Undo();
}
}
public void LongTimeHandleAsync(string confirmFile)
{
curIndentity = System.Security.Principal.WindowsIdentity.GetCurrent();
AsyncConfirmMultipleCall handle = new AsyncConfirmMultipleCall(LongTimeHandle);
handle.BeginInvoke(confirmFile, new AsyncCallback(CallbackMethod), handle);
}
private void CallbackMethod(IAsyncResult ar)
{
AsyncConfirmMultipleCall handle = ar.AsyncState as AsyncConfirmMultipleCall;
handle.EndInvoke(ar);
}
}
这里提供了异步方法LongTimeHandleAsync。
在开始异步调用之前,先取得当前线程的user信息curIndentity.然后最关键的代码就是:
WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(curIndentity.Token);
在这里,就开始了身份的模拟。这是WindowsIdentity的静态方法,它也提供有实例方法。将之前user的Token传入进行模拟。特别要注意,在代码结束前一定要恢复用户的标识,所以,比较好的做法是在try/catch/finally的finally块中调用impersonatedUser.Undo()
如果不进行身份模拟,string userName的值会是"ASPNET"(或"NETWORK SERVICE")而不是用户的Window user.