我需要将文件从服务器下载到共享驱动器目录,如果该目录不存在,则创建该目录。有几件事使事情变得更加复杂:

  • 我没有对共享驱动器目录的写访问权限(也不会在UAT/Prod中运行该作业的帐户)。
  • 具有共享访问权限的服务帐户在共享驱动器目录中没有任何特权。

  • 我试图模仿这样:
    class Impersonation
    {
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON_TYPE_NEW_CREDENTIALS = 9;
        const int LOGON32_PROVIDER_WINNT50 = 3;
    
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    
        public static void Impersonate(string domain, string user, string password, Action act)
        {
            //if no user specified, don't impersonate
            if (user.Trim() == "")
            {
                act();
                return;
            }
            WindowsImpersonationContext impersonationContext = null;
            IntPtr token = IntPtr.Zero;
            try
            {
                //if no domain specified, default it to current machine
                if (domain.Trim() == "")
                {
                    domain = System.Environment.MachineName;
                }
                bool result = LogonUser(user, domain, password, LOGON_TYPE_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, ref token);
                WindowsIdentity wi = new WindowsIdentity(token);
                impersonationContext = WindowsIdentity.Impersonate(token);
                act();
            }
            catch (Exception ex)
            {
                if (impersonationContext != null)
                {
                    impersonationContext.Undo();
                    impersonationContext = null;
                }
                //if something went wrong, try it as the running user just in case
                act();
            }
            finally
            {
                if (impersonationContext != null)
                {
                    impersonationContext.Undo();
                    impersonationContext = null;
                }
                if (token != IntPtr.Zero)
                {
                    CloseHandle(token);
                    token = IntPtr.Zero;
                }
            }
        }
    }
    

    一段实际的调用代码是(在另一个类中):
    private static void CreateDirectoryIfNotExist(string directory, string domain, string username, string password)
    {
        Impersonation.Impersonate(domain, username, password, () => CreateIfNotExist(directory));
    }
    
    private static void CreateIfNotExist(string dir)
    {
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
    }
    

    如果使用服务帐户的正确登录信息运行它,则会在Directory.CreateDirectory(string)调用上收到异常:



    我猜这意味着不允许该服务帐户登录到我已经知道的执行计算机。但实际上,没有理由需要登录到执行计算机。有没有一种方法可以使用模拟登录到远程计算机并从那里执行命令?

    最佳答案

    如果该帐户无法登录,则无法进行模拟操作。模拟需要线程在用户凭据下运行。这就是LogonUser失败的原因。

    您可以使用WNetAddConnection2函数来建立与网络资源的连接。

    这是使用这种方法的CreateDirectoryIfNotExist函数的示例:

    public static void CreateDirectoryIfNotExists(string directory, string sharePath, string username, string password)
    {
       NETRESOURCE nr = new NETRESOURCE();
       nr.dwType = ResourceType.RESOURCETYPE_DISK;
       nr.lpLocalName = null;
       nr.lpRemoteName = sharePath;
       nr.lpProvider = null;
    
       int result = WNetAddConnection2(nr, password, username, 0);
       string directoryFullPath = Path.Combine(sharePath, directory);
       if (!Directory.Exists(directoryFullPath))
       {
          Directory.CreateDirectory(directoryFullPath);
       }
    }
    

    为了能够进行系统调用,您还需要pinvoke.net中的以下定义。
    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
       public ResourceScope dwScope = 0;
       public ResourceType dwType = 0;
       public ResourceDisplayType dwDisplayType = 0;
       public ResourceUsage dwUsage = 0;
       [MarshalAs(UnmanagedType.LPStr)] public string lpLocalName = null;
       [MarshalAs(UnmanagedType.LPStr)] public string lpRemoteName = null;
       [MarshalAs(UnmanagedType.LPStr)] public string lpComment = null;
       [MarshalAs(UnmanagedType.LPStr)] public string lpProvider;
    };
    public enum ResourceScope
    {
       RESOURCE_CONNECTED = 1,
       RESOURCE_GLOBALNET,
       RESOURCE_REMEMBERED,
       RESOURCE_RECENT,
       RESOURCE_CONTEXT
    };
    
    public enum ResourceType
    {
       RESOURCETYPE_ANY,
       RESOURCETYPE_DISK,
       RESOURCETYPE_PRINT,
       RESOURCETYPE_RESERVED
    };
    
    public enum ResourceUsage
    {
       RESOURCEUSAGE_CONNECTABLE = 0x00000001,
       RESOURCEUSAGE_CONTAINER = 0x00000002,
       RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
       RESOURCEUSAGE_SIBLING = 0x00000008,
       RESOURCEUSAGE_ATTACHED = 0x00000010,
       RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    };
    
    public enum ResourceDisplayType
    {
       RESOURCEDISPLAYTYPE_GENERIC,
       RESOURCEDISPLAYTYPE_DOMAIN,
       RESOURCEDISPLAYTYPE_SERVER,
       RESOURCEDISPLAYTYPE_SHARE,
       RESOURCEDISPLAYTYPE_FILE,
       RESOURCEDISPLAYTYPE_GROUP,
       RESOURCEDISPLAYTYPE_NETWORK,
       RESOURCEDISPLAYTYPE_ROOT,
       RESOURCEDISPLAYTYPE_SHAREADMIN,
       RESOURCEDISPLAYTYPE_DIRECTORY,
       RESOURCEDISPLAYTYPE_TREE,
       RESOURCEDISPLAYTYPE_NDSCONTAINER
    };
    public enum ResourceConnection
    {
       CONNECT_UPDATE_PROFILE = 1,
       CONNECT_UPDATE_RECENT = 2,
       CONNECT_TEMPORARY = 4,
       CONNECT_INTERACTIVE = 8,
       CONNECT_PROMPT = 0X10,
       CONNECT_REDIRECT = 0X80,
       CONNECT_CURRENT_MEDIA = 0X200,
       CONNECT_COMMAND_LINE = 0X800,
       CONNECT_CMD_SAVECRED = 0X1000,
       CONNECT_CRED_RESET = 0X2000
    
    };
    
    [DllImport("mpr.dll", CharSet = System.Runtime.InteropServices.CharSet.Ansi)]
    private static extern int WNetAddConnection2(NETRESOURCE lpNetResource,
          [MarshalAs(UnmanagedType.LPStr)]  string lpPassword,
          [MarshalAs(UnmanagedType.LPStr)]  string lpUserName, int dwFlags);
    

    您可以将此定义添加到与函数相同的类中。

    这也是两个使用相同方法的旧帖子的链接。

    Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials

    How do I access a file share programmatically

    关于c# - 如何使用模拟功能在UNC远程计算机上操纵文件/目录?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58207621/

    10-11 20:41
    查看更多