四、角色模拟
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.IO;
namespace Test
{
public class Test
{
// logon types
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
// logon providers
const int LOGON32_PROVIDER_DEFAULT = 0;
const int LOGON32_PROVIDER_WINNT50 = 3;
const int LOGON32_PROVIDER_WINNT40 = 2;
const int LOGON32_PROVIDER_WINNT35 = 1;
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
private WindowsImpersonationContext impersonationContext;
public bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
// 这里使用LOGON32_LOGON_NEW_CREDENTIALS来访问远程资源。
// 如果要(通过模拟用户获得权限)实现服务器程序,访问本地授权数据库可
// 以用LOGON32_LOGON_INTERACTIVE
if (LogonUser(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
IPrincipal pr = System.Threading.Thread.CurrentPrincipal;
IIdentity id = pr.Identity;
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
public void undoImpersonation()
{
impersonationContext.Undo();
}
public void TestFunc()
{
bool isImpersonated = false;
try
{
if (impersonateValidUser("UserName", "Domain", "Password"))
{
isImpersonated = true;
//do what you want now, as the special user
// ...
File.Copy(@"\192.168.1.48generals ow.htm", "c:\now.htm", true);
}
}
finally
{
if (isImpersonated)
undoImpersonation();
}
}
}
}
  五、比较
  方法一通过调用Shell命令Net Use实现,有点笨拙。
  方法二和方法一有些相似之处。映射远程资源,然后访问。
  方法三由于会有超时异常出现,所以在网络速度快、传输小文件时是可以的。
  方法四通过身份模拟实现远程资源访问。一些服务器进程是通过这种方式运行的。这种方法也是我的爱。
  六、要注意的地方
  关于这几种方法,google后都可以找到一些文章。但是等到自己实际测试时,有时会出现各种小错误,
  这些错误基本来源于两方面:
  1、函数的参数选择有问题,和自己的环境不相符。
  比如
  public static extern int LogonUser(String lpszUserName,
  String lpszDomain,
  String lpszPassword,
  int dwLogonType,
  int dwLogonProvider,
  ref IntPtr phToken);
  中的dwLogonType,要访问远程资源要用LOGON32_LOGON_NEW_CREDENTIALS,
  要模拟本机用户要用LOGON32_LOGON_INTERACTIVE。
  2、函数的参数格式有问题。
  a、比如
  public static extern int LogonUser(String lpszUserName,
  String lpszDomain,
  String lpszPassword,
  int dwLogonType,
  int dwLogonProvider,
  ref IntPtr phToken);
  中的lpszUserName、lpszDomain、lpszPassword要写清楚。
  我在这遇到过问题,第一次测试时,远程服务器是一台独立的文件服务器,这是我的调用方式:
  LogonUser("myname", "192.168.1.48", "password", LOGON32_LOGON_NEW_CREDENTIALS,
  LOGON32_PROVIDER_DEFAULT, ref token);
  第二次测试时,远程服务器是域MyDomain中的一个成员服务器,提供文件服务。这时代码应该是:
  LogonUser("myname", "MyDomain", "password", LOGON32_LOGON_NEW_CREDENTIALS,
  LOGON32_PROVIDER_DEFAULT, ref token);
  注意,代码中是MyDomain而不是IP地址。
  b、再如:
  参考上面代码
  string remote = @"\192.168.1.48generals";
  string local = @"P:";
  string username = @"DomainUserName";
  string password = @"Password";
  如果@"\192.168.1.48generals"变成@"\192.168.1.48generals”会出错;
  如果是域中的用户,那么把@"DomainUserName"变成@"UserName"会出错。