You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用WindowsIdentity.RunImpersonated执行文件操作时遇访问拒绝错误

C#模拟用户身份执行文件操作时遇到访问拒绝问题

我尝试在C#中通过模拟用户身份对文件系统执行上传、下载、删除等操作,采用WindowsIdentity.RunImpersonated方法委托权限,但一直遇到**访问拒绝(Access Denied)**错误,以下是我的实现代码:

实现代码

ImpersonationManager类

public class ImpersonationManager
{
    private SafeAccessTokenHandle _safeAccessTokenHandle;

    private readonly string _domainName;
    private readonly string _userName;
    private readonly string _password;

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
   int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);

    public ImpersonationManager(string domainName, string userName, string password)
    {
        _domainName = domainName;
        _userName = userName;   
        _password = password;
    }   

    public SafeAccessTokenHandle GetSafeAccessTokenHandle()
    {

        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_INTERACTIVE = 2;
        try
        {
            bool returnValue = LogonUser(_userName, _domainName, _password,
                               LOGON32_PROVIDER_DEFAULT, LOGON32_LOGON_INTERACTIVE,
                               out _safeAccessTokenHandle);

            if (!returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(ret, $"LogonUser failed with error code : {ret}");
            }
        }
        catch (Exception ex)
        {
            throw;            
        }    

        return _safeAccessTokenHandle;
    }

}

UploadFileAsync方法

public async Task<bool> UploadFileAsync(string filePath = null, File file = null, Stream fileStream = null)
{
    if (file == null)
        throw new ArgumentNullException(nameof(file));

    if (fileStream == null)
        throw new ArgumentNullException(nameof(fileStream));

    SafeAccessTokenHandle safeAccessTokenHandle = GetSafeAccessTokenHandleAsync();

    WindowsIdentity.RunImpersonated(safeAccessTokenHandle,() =>
    {
        var fileName = file.FileName;

        try
        {
            using (FileStream destinationStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                fileStream.CopyTo(destinationStream);
                return true;
            }
           
        }
        catch (Exception ex)
        {
            throw;
        }

    });

    return true;

}

问题分析及解决办法

核心问题点

  1. LogonUser参数顺序错误:调用LogonUser时,将dwLogonType和dwLogonProvider的顺序搞反了。正确参数顺序应为:用户名、域名、密码、登录类型、登录提供程序、输出令牌。当前错误的参数传递会导致获取的令牌权限不足或登录失败。
  2. 异步方法调用问题:UploadFileAsync是异步方法,但调用GetSafeAccessTokenHandleAsync(类中实际为同步方法GetSafeAccessTokenHandle)未加await,会导致令牌未获取完成就开始模拟操作;同时WindowsIdentity.RunImpersonated不支持直接传入异步委托,需在委托内部处理异步逻辑。
  3. 令牌资源未释放:类级别字段_safeAccessTokenHandle未正确释放,易造成资源泄漏,影响后续权限验证。
  4. 登录类型适配问题:LOGON32_LOGON_INTERACTIVE适用于交互式场景,若为服务端或非交互式环境,需更换为LOGON32_LOGON_NETWORK_CLEARTEXT(8)或LOGON32_LOGON_BATCH(4)。

修正后的代码示例

修正ImpersonationManager类(含资源释放)

public class ImpersonationManager : IDisposable
{
    private SafeAccessTokenHandle _safeAccessTokenHandle;

    private readonly string _domainName;
    private readonly string _userName;
    private readonly string _password;

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
                                       int dwLogonType, int dwLogonProvider, out SafeAccessTokenHandle phToken);

    public ImpersonationManager(string domainName, string userName, string password)
    {
        _domainName = domainName;
        _userName = userName;   
        _password = password;
    }   

    public SafeAccessTokenHandle GetSafeAccessTokenHandle()
    {
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_LOGON_INTERACTIVE = 2;
        try
        {
            // 修正参数顺序:登录类型在前,登录提供程序在后
            bool returnValue = LogonUser(_userName, _domainName, _password,
                               LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                               out _safeAccessTokenHandle);

            if (!returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(ret, $"LogonUser失败,错误码:{ret}");
            }
        }
        catch (Exception ex)
        {
            throw;            
        }    

        return _safeAccessTokenHandle;
    }

    // 实现IDisposable接口释放令牌资源
    public void Dispose()
    {
        _safeAccessTokenHandle?.Dispose();
    }
}

修正UploadFileAsync方法

public async Task<bool> UploadFileAsync(string filePath, File file, Stream fileStream)
{
    if (file == null)
        throw new ArgumentNullException(nameof(file));
    if (fileStream == null)
        throw new ArgumentNullException(nameof(fileStream));
    if (string.IsNullOrEmpty(filePath))
        throw new ArgumentNullException(nameof(filePath));

    using (var impersonationManager = new ImpersonationManager("你的域名", "用户名", "密码"))
    {
        var safeAccessTokenHandle = impersonationManager.GetSafeAccessTokenHandle();
        bool uploadSuccess = false;

        WindowsIdentity.RunImpersonated(safeAccessTokenHandle, () =>
        {
            try
            {
                using (FileStream destinationStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
                {
                    // 若需异步操作,使用CopyToAsync并等待
                    fileStream.CopyTo(destinationStream);
                    uploadSuccess = true;
                }
            }
            catch (Exception ex)
            {
                throw new IOException("文件上传失败", ex);
            }
        });

        return uploadSuccess;
    }
}

额外注意事项

  • 确保运行程序的进程具备调用LogonUser的权限,通常需要管理员权限或“作为操作系统的一部分登录”权限。
  • 调试时可在RunImpersonated委托内调用WindowsIdentity.GetCurrent(),打印当前用户确认模拟是否生效。
  • 目标文件路径需给模拟用户配置足够的读写权限,远程共享目录需同时检查共享权限和NTFS权限。

内容的提问来源于stack exchange,提问作者Jack Rono dez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:05:12