使用用户名和密码连接网络驱动器

28

我该如何在.NET中提供凭据以连接到网络驱动器?

我正在尝试从网络驱动器检索文件,需要提供用户凭据才能访问驱动器。


1
我相信有一种更通用的方法:https://dev59.com/ZHVC5IYBdhLWcg3wbglT#39540451 - Pavel Kovalev
有一个叫做SMBLibrary的图书馆,这可能是一个更好的方式。 - undefined
8个回答

20

这个启发,非常优雅的解决方案。该解决方案仅使用 .Net 库,无需使用任何命令行或 Win32 API。

供参考的代码:

NetworkCredential theNetworkCredential = new NetworkCredential(@"domain\username", "password");
CredentialCache theNetCache = new CredentialCache();
theNetCache.Add(new Uri(@"\\computer"), "Basic", theNetworkCredential);
string[] theFolders = Directory.GetDirectories(@"\\computer\share");

@VladL,您能否使用Windows资源管理器手动访问网络共享?您是否拥有所需用户的权限? - zendu
1
我不得不对此进行两个更改,才能使其在我的网络上正常工作。我必须使用带有不同域参数的NetworkCredential重载。我还必须从基本身份验证切换到摘要身份验证。 - BenMaddox
13
我觉得很奇怪,我们正在创建一个CredentialsCache实例,但却没有真正用到这个对象。如果这是一个全局存储(所有操作都会自动访问),我希望能够获取一些单例对象。但既然没有,我认为在这里我们实际上并没有使用凭据。 - Dustin Oprea
1
我得到了一个错误的正面反馈。它之所以能够工作,是因为我映射了共享。一旦它消失了,我开始收到拒绝异常。 - MiguelSlv
1
虽然我晚了,但感谢@DustinOprea指出这是无意义的。这个答案为什么有这么多赞超出了我的理解...它是否适用于没有共享映射或Windows凭据管理器已经配置的任何人? - matpop
显示剩余3条评论

14

最好的方法是使用p/invoke WNetUseConnection

[StructLayout(LayoutKind.Sequential)] 
private class NETRESOURCE
{ 
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
}


[DllImport("Mpr.dll")] 
private static extern int WNetUseConnection(
            IntPtr hwndOwner,
            NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUserID,
            int dwFlags,
            string lpAccessName,
            string lpBufferSize,
            string lpResult
        );

这里有示例代码


对我来说不起作用。连接时没有错误,但是在访问文件夹时,程序会永远卡住。 - ciencia

5

你可以使用模拟环境,特别是WindowsIdentity类(带有登录令牌),在读写文件时进行模拟。

var windowsIdentity = new WindowsIdentity(logonToken);
using (var impersonationContext = windowsIdentity.Impersonate()) {
    // Connect, read, write
}

4
只能在Windows环境下良好运行。当您开始使用AS400、Solaris和Linux时,特别是如果共享需要除了运行应用程序的凭据之外的其他凭据时,会变得麻烦。 - Riaan
1
bzlm的解决方案是可行的,但他没有详细说明。从https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext.aspx获取一些代码,特别是safeTokenHandle类和相关依赖项。 - Jan Jansz
这段代码在本地系统上可以运行,但在使用匿名池身份的IIS上却无法正常工作,有什么建议吗? - Abhishek Tomar
我实际上正在寻找解决这个问题的方法。我的情况是,我使用这种模拟来使用服务用户的安全上下文注册证书,但我无法从共享中获取文件。奇怪的是,如果我直接以服务用户身份登录,然后切换回运行模拟的程序的用户,则可以正常工作。我最好的猜测是,即使本地上下文这样做,Kerberos也拒绝承认模拟,但如果您有缓存的票证,则共享访问将起作用。 - AJ Henderson
话说,我的问题可能只是使用了服务登录类型。用于模拟的登录类型似乎至关重要。 - AJ Henderson

1

我真的不知道隐藏的过程,但我使用了一个webrequest,这样我就能够传递凭据,对我来说完美地解决了问题。

var ftpDownloadFile = WebRequest.Create("filePath");
ftpDownloadFile.Method = WebRequestMethods.Ftp.DownloadFile;
ftpDownloadFile.Credentials = new NetworkCredential("user", "pass");
using (var reader = (FtpWebResponse)ftpDownloadFile.GetResponse())
using (var responseStream = reader.GetResponseStream())
{
    var writeStream = new FileStream(Path.Combine(LocalStorage), FileMode.Create);
    const int length = 2048;
    var buffer = new Byte[length];
    if (responseStream != null)
    {
        var bytesRead = responseStream.Read(buffer, 0, length);
        while (bytesRead > 0)
        {
            writeStream.Write(buffer, 0, bytesRead);
            bytesRead = responseStream.Read(buffer, 0, length);
        }
    }
    reader.Close();
    writeStream.Close();
}

1

使用此代码进行模拟操作,在MVC.NET中已经测试过,如果您想在dot net core中使用,可能需要进行一些更改。如果您需要在dot net core中使用,请告诉我,我会分享相关信息。

 public static class ImpersonationAuthenticationNew
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string usernamee, string domain, string password, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);
        [DllImport("kernel32.dll")]
        private static extern bool CloseHandle(IntPtr hObject);
        public static bool Login(string domain,string username, string password)
        {                
            IntPtr token = IntPtr.Zero;
            var IsSuccess = LogonUser(username, domain, password, LogonType.LOGON32_LOGON_NEW_CREDENTIALS, LogonProvider.LOGON32_PROVIDER_WINNT50, ref token);
            if (IsSuccess)
            {
                using (WindowsImpersonationContext person = new WindowsIdentity(token).Impersonate())
                {
                    var xIdentity = WindowsIdentity.GetCurrent();
                    #region Start ImpersonationContext  Scope
                    try
                    {

                        // TYPE YOUR CODE HERE 
                      return true;
                    }
                    catch (Exception ex) { throw (ex); }
                    finally {
                        person.Undo();
                        CloseHandle(token);
                       
                    }
                    #endregion
                }
            }
            return false;
        }
    }

    #region Enums
    public enum LogonType
    {
        /// <summary>
        /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on  
        /// by a terminal server, remote shell, or similar process.
        /// This logon type has the additional expense of caching logon information for disconnected operations;
        /// therefore, it is inappropriate for some client/server applications,
        /// such as a mail server.
        /// </summary>
        LOGON32_LOGON_INTERACTIVE = 2,

        /// <summary>
        /// This logon type is intended for high performance servers to authenticate plaintext passwords.

        /// The LogonUser function does not cache credentials for this logon type.
        /// </summary>
        LOGON32_LOGON_NETWORK = 3,

        /// <summary>
        /// This logon type is intended for batch servers, where processes may be executing on behalf of a user without
        /// their direct intervention. This type is also for higher performance servers that process many plaintext
        /// authentication attempts at a time, such as mail or Web servers.
        /// The LogonUser function does not cache credentials for this logon type.
        /// </summary>
        LOGON32_LOGON_BATCH = 4,

        /// <summary>
        /// Indicates a service-type logon. The account provided must have the service privilege enabled.
        /// </summary>
        LOGON32_LOGON_SERVICE = 5,

        /// <summary>
        /// This logon type is for GINA DLLs that log on users who will be interactively using the computer.
        /// This logon type can generate a unique audit record that shows when the workstation was unlocked.
        /// </summary>
        LOGON32_LOGON_UNLOCK = 7,

        /// <summary>
        /// This logon type preserves the name and password in the authentication package, which allows the server to make
        /// connections to other network servers while impersonating the client. A server can accept plaintext credentials
        /// from a client, call LogonUser, verify that the user can access the system across the network, and still
        /// communicate with other servers.
        /// NOTE: Windows NT:  This value is not supported.
        /// </summary>
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8,

        /// <summary>
        /// This logon type allows the caller to clone its current token and specify new credentials for outbound connections.
        /// The new logon session has the same local identifier but uses different credentials for other network connections.
        /// NOTE: This logon type is supported only by the LOGON32_PROVIDER_WINNT50 logon provider.
        /// NOTE: Windows NT:  This value is not supported.
        /// </summary>
        LOGON32_LOGON_NEW_CREDENTIALS = 9,
    }
    public enum LogonProvider
    {
        /// <summary>
        /// Use the standard logon provider for the system.
        /// The default security provider is negotiate, unless you pass NULL for the domain name and the user name
        /// is not in UPN format. In this case, the default provider is NTLM.
        /// NOTE: Windows 2000/NT:   The default security provider is NTLM.
        /// </summary>
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    }
    #endregion

0
var fileName = "Mylogs.log";
var local = Path.Combine(@"C:\TempLogs", fileName);
var remote = Path.Combine(@"\\servername\c$\Windows\Temp\", fileName);

WebClient request = new WebClient();
request.Credentials = new NetworkCredential(@"username", "password");

if (File.Exists(local))
{
    File.Delete(local);

    File.Copy(remote, local, true);
}
else
{
    File.Copy(remote, local, true);
}

-2

你可以使用 system.diagnostocs.process 调用 'net use .... with userid and password' 或者带有这些参数的命令行。


你可以这么做,但是你想要这样做吗? - Steve Townsend
如果它实现了您的用例目标,那么为什么不行呢?这是一行代码,可以实现所需的结果。它具有内置的所有错误处理功能,并经过验证和测试。重新编写功能似乎很愚蠢。 - Preet Sangha
3
如果它达到了您使用情况的目的,那么它就是完全有效的 - 这是一个非常笼统的说法,在我看来,您在这里的回答是一个反例。启动一个新进程来执行可以通过单个P/Invoke完成的操作(无需重写)是杀鸡焉用牛刀。 - Steve Townsend

-2
你可以使用WebClient类来使用凭据连接到网络驱动程序。请包含以下命名空间:
using System.Net;

WebClient request = new WebClient();
request.Credentials = new NetworkCredential("domain\username", "password");
string[] theFolders = Directory.GetDirectories(@"\\computer\share");

Directory.GetDirectories与WebClient没有任何关系 - 它们只是您示例中相邻的代码行。 - alastairtree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接