用户模拟在.NET中的编程实现
发布日期:2007-07-18 | 更新日期:2009-03-14
作者:郑佐
摘要: 本文简单介绍在.NET中 进行Windows用户模拟设置和编程实现 。
本页内容
在日常开发中经常会碰到这样的需求,在执行系统的某一部分功能代码时需要高于当前登录Windows用户的权限,一般常见的解决方案是使用身份模拟。本文通过简单的示例来讲解在.NET中实现模拟特定Windows用户执行特定操作的功能。
在ASP.NET中,一种常见的方式是通过配置Web.Config来实现身份模拟。通过MSDN文档我们不难找到详细的描述。
在<system.web>元素下面放置以下元素节点:
<identity impersonate="true|false" userName="domain/username" password="password"/>
|
简单的配置示例如下:
<identity impersonate="true" userName="DomainWeb/ASPNETOwner" password="Password"/>
|
上面的配置用户名和密码是通过明文保存的,因此存在安全隐患。值得关注的一种安全实现方式是把用户名和密码通过REG_BINARY注册表键类型格式保存到注册表的HKLM子项中。对于数据的加密通过Aspnet_setreg.exe工具来实现。
对于加密后的节点示例如下:
<identity impersonate="true" userName="registry:HKLM/Software/AspNetProcess,Name"password="registry:HKLM/Software/AspNetProcess,Pwd"/>
|
如果需要通过代码提供更多的灵活性,System.Security.Principal命名空间下的WindowsImpersonationContext类提供了对应的编程接口。
对于身份模拟来说,有时候需要特定的用户执行一些特定的代码,特别是在客户端程序应用中可能会存在这样的需求,那怎么实现呢?在Microsoft的客户支持和帮助的网站上提供了
实现方法
。为了让代码感觉上更友好,本文对其进行了简单的重构,整个功能通过ImpersonationHelper类来包装。
首先需要引用以下命名空间:
using
System;
using
System.Security.Principal;
using
System.Runtime.InteropServices;
|
另外,在实现过程中用到了以下平台调用:
[DllImport("advapi32.dll")]
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
类 中主要包含两个公共方法。Impersonate方法启用身份模拟,可以输入“域名/用户名称”和“密码”。如果模拟成功,返回true。否则模拟失败返 回false。EndImpersonate方法同Impersonate方法成对出现,通过调用该方法结束指定身份的模拟。
public
bool Impersonate(String userName, String password);
public
void EndImpersonate();
|
以下是ImpersonationHelper类的定义。
public
sealed class ImpersonationHelper
{
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
private WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll")]
private static extern int LogonUserA(String lpszUserName,String lpszDomain,String lpszPassword,int dwLogonType,int dwLogonProvider,ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int DuplicateToken(IntPtr hToken,int impersonationLevel,ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool CloseHandle(IntPtr handle);
public ImpersonationHelper()
{
}
private bool Impersonate(String userName, String domain, String password)
{
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
WindowsIdentity tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
public bool Impersonate(String userName, String password)
{
if (userName.IndexOf('//') != -1)
{
string[] s = userName.Split('//');
return Impersonate(s[1], s[0], password);
}
return Impersonate(userName, null, password);
}
public void EndImpersonate()
{
impersonationContext.Undo();
}
}
|
下面是一个简单通过ImpersonationHelper类实现身份模拟的示例,具体逻辑在CallMethod中。
class
ClientCaller
{
public void CallMethod()
{
ImpersonationHelper helper = new ImpersonationHelper();
if (helper.Impersonate("domain//username", "password"))
{
//
在这里插入需要执行模拟用户权限的代码。
//
代码...
helper.EndImpersonate();//
结束身份模拟。
}
else
{
//
如果模拟失败在这里处理。
}
}
}
|
测试过程中可以采用以下设置方案。
在系统中建立不同的目录(假设为NTFS文件系统),在文件夹的属性对话框中选择安全选项卡,设置特定的帐户权限,在代码中模拟不同的用户访问这些文件夹来验证模拟的正确性。
查看ImpersonationHelper的实现,不难发现在复杂的应用中需要注意的几个问题:
(1)
ImpersonationHelper
不是线程安全的;
(2)ImpersonationHelper
的两个方法
Impersonate
和EndImpersonate必须成对出现,而且不能嵌套调用。
另外,对于ImpersonationHelper类的两个方法改为静态,或者实现一个传递delegate的
Impersonate
重载方法,使用起来是不是会更有趣一些,当然更可以完全改造ImpersonationHelper类。