当连接到当前用户(在我的情况下,支持网络的服务用户)没有权限的网络共享时,必须提供名称和密码.
我知道如何使用Win32函数(WNet*
来自的家族mpr.dll
),但是想用.Net(2.0)功能.
有哪些选择?
也许更多信息有助于:
用例是Windows服务,而不是Asp.Net应用程序.
该服务在没有共享权限的帐户下运行.
客户端不知道共享所需的用户帐户.
客户端和服务器不是同一域的成员.
Luke Quinane.. 307
我非常喜欢马克·布拉克特的回答,所以我做了自己的快速实施.在这里,如果其他人急需它:
public class NetworkConnection : IDisposable { string _networkName; public NetworkConnection(string networkName, NetworkCredential credentials) { _networkName = networkName; var netResource = new NetResource() { Scope = ResourceScope.GlobalNetwork, ResourceType = ResourceType.Disk, DisplayType = ResourceDisplaytype.Share, RemoteName = networkName }; var userName = string.IsNullOrEmpty(credentials.Domain) ? credentials.UserName : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName); var result = WNetAddConnection2( netResource, credentials.Password, userName, 0); if (result != 0) { throw new Win32Exception(result); } } ~NetworkConnection() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { WNetCancelConnection2(_networkName, 0, true); } [DllImport("mpr.dll")] private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags); [DllImport("mpr.dll")] private static extern int WNetCancelConnection2(string name, int flags, bool force); } [StructLayout(LayoutKind.Sequential)] public class NetResource { public ResourceScope Scope; public ResourceType ResourceType; public ResourceDisplaytype DisplayType; public int Usage; public string LocalName; public string RemoteName; public string Comment; public string Provider; } public enum ResourceScope : int { Connected = 1, GlobalNetwork, Remembered, Recent, Context }; public enum ResourceType : int { Any = 0, Disk = 1, Print = 2, Reserved = 8, } public enum ResourceDisplaytype : int { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05, Network = 0x06, Root = 0x07, Shareadmin = 0x08, Directory = 0x09, Tree = 0x0a, Ndscontainer = 0x0b }
它应该是`throw new Win32Exception(result);`,因为WNetAddConnection2返回win32错误代码(`ERROR_XXX`) (10认同)
为了使上面的代码能够编译,需要使用以下using语句:using System.Net; 使用System.Runtime.InteropServices; 使用System.ComponentModel; (3认同)
这是一段很棒的代码。需要登录到UNIX系统以获得用于打印到MVC5 Web应用程序的目录列表,这确实可以解决问题。+1 !!! (2认同)
抱歉刷新那个旧线程,但看起来它在块完成后没有关闭连接.我有一个程序上传几张图片,第一个很好,第二个给失败.程序关闭时释放连接.有什么建议吗? (2认同)
Mark Bracket.. 150
您可以更改线程标识,也可以更改P/Invoke WNetAddConnection2.我更喜欢后者,因为我有时需要为不同的位置维护多个凭据.我将它包装成IDisposable并调用WNetCancelConnection2以后删除信用(避免多个用户名错误):
using (new NetworkConnection(@"\\server\read", readCredentials)) using (new NetworkConnection(@"\\server2\write", writeCredentials)) { File.Copy(@"\\server\read\file", @"\\server2\write\file"); }
readCredentials和writeCredentials的定义可以包含在答案中. (7认同)
该服务不是目标域的成员 - 模拟无法工作,因为您无法在本地创建安全令牌并使用它进行模拟.PInvoke是唯一的*方式. (4认同)
Pavel Kovale.. 46
7年后的今天,我面临同样的问题,我想分享我的解决方案版本.
它是复制和粘贴准备:-)这里是:
步骤1在您的代码中(每当您需要使用权限执行某些操作时)
ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate { //Your code here //Let's say file copy: if (!File.Exists(to)) { File.Copy(from, to); } });第2步
帮助文件做了一个魔术
using System; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Security.Principal; using Microsoft.Win32.SafeHandles; namespace BlaBla { public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeTokenHandle() : base(true) { } [DllImport("kernel32.dll")] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle); protected override bool ReleaseHandle() { return CloseHandle(handle); } } public class ImpersonationHelper { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private extern static bool CloseHandle(IntPtr handle); [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute) { SafeTokenHandle safeTokenHandle; try { const int LOGON32_PROVIDER_DEFAULT = 0; //This parameter causes LogonUser to create a primary token. const int LOGON32_LOGON_INTERACTIVE = 2; // Call LogonUser to obtain a handle to an access token. bool returnValue = LogonUser(userName, domainName, userPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle); //Facade.Instance.Trace("LogonUser called."); if (returnValue == false) { int ret = Marshal.GetLastWin32Error(); //Facade.Instance.Trace($"LogonUser failed with error code : {ret}"); throw new System.ComponentModel.Win32Exception(ret); } using (safeTokenHandle) { //Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}"); //Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}"); // Use the token handle returned by LogonUser. using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())) { using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { //Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}"); //Facade.Instance.Trace("Start executing an action"); actionToExecute(); //Facade.Instance.Trace("Finished executing an action"); } } //Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}"); } } catch (Exception ex) { //Facade.Instance.Trace("Oh no! Impersonate method failed."); //ex.HandleException(); //On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/ throw; } } } }
为什么我收到登录失败错误..? (4认同)
@MohammadRashid根据[LogonUser](https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v = vs.85).aspx)上的文档,它仅适用于本地计算机:"LogonUser函数尝试将用户登录到本地计算机.本地计算机是调用LogonUser的计算机.您无法使用LogonUser登录到远程计算机."您将收到错误" Win32Exception:用户名或密码不正确." 所以我认为机器至少需要在同一个域上. (2认同)
Hakan KOSE.. 28
我搜索了很多方法,我按照自己的方式做了.您必须通过命令提示符NET USE命令打开两台机器之间的连接,并在完成工作后清除与命令提示符NET USE"myconnection"/ delete的连接.
您必须使用代码后面的命令提示符进程,如下所示:
var savePath = @"\\servername\foldername\myfilename.jpg"; var filePath = @"C:\\temp\myfileTosave.jpg";
用法很简单:
SaveACopyfileToServer(filePath, savePath);
这是功能:
using System.IO using System.Diagnostics; public static void SaveACopyfileToServer(string filePath, string savePath) { var directory = Path.GetDirectoryName(savePath).Trim(); var username = "loginusername"; var password = "loginpassword"; var filenameToSave = Path.GetFileName(savePath); if (!directory.EndsWith("\\")) filenameToSave = "\\" + filenameToSave; var command = "NET USE " + directory + " /delete"; ExecuteCommand(command, 5000); command = "NET USE " + directory + " /user:" + username + " " + password; ExecuteCommand(command, 5000); command = " copy \"" + filePath + "\" \"" + directory + filenameToSave + "\""; ExecuteCommand(command, 5000); command = "NET USE " + directory + " /delete"; ExecuteCommand(command, 5000); }
并且ExecuteCommand函数是:
public static int ExecuteCommand(string command, int timeout) { var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command) { CreateNoWindow = true, UseShellExecute = false, WorkingDirectory = "C:\\", }; var process = Process.Start(processInfo); process.WaitForExit(timeout); var exitCode = process.ExitCode; process.Close(); return exitCode; }
这个功能对我来说非常快速和稳定.
我非常喜欢马克·布拉克特的回答,所以我做了自己的快速实施.在这里,如果其他人急需它:
public class NetworkConnection : IDisposable { string _networkName; public NetworkConnection(string networkName, NetworkCredential credentials) { _networkName = networkName; var netResource = new NetResource() { Scope = ResourceScope.GlobalNetwork, ResourceType = ResourceType.Disk, DisplayType = ResourceDisplaytype.Share, RemoteName = networkName }; var userName = string.IsNullOrEmpty(credentials.Domain) ? credentials.UserName : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName); var result = WNetAddConnection2( netResource, credentials.Password, userName, 0); if (result != 0) { throw new Win32Exception(result); } } ~NetworkConnection() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { WNetCancelConnection2(_networkName, 0, true); } [DllImport("mpr.dll")] private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags); [DllImport("mpr.dll")] private static extern int WNetCancelConnection2(string name, int flags, bool force); } [StructLayout(LayoutKind.Sequential)] public class NetResource { public ResourceScope Scope; public ResourceType ResourceType; public ResourceDisplaytype DisplayType; public int Usage; public string LocalName; public string RemoteName; public string Comment; public string Provider; } public enum ResourceScope : int { Connected = 1, GlobalNetwork, Remembered, Recent, Context }; public enum ResourceType : int { Any = 0, Disk = 1, Print = 2, Reserved = 8, } public enum ResourceDisplaytype : int { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05, Network = 0x06, Root = 0x07, Shareadmin = 0x08, Directory = 0x09, Tree = 0x0a, Ndscontainer = 0x0b }
您可以更改线程标识,也可以更改P/Invoke WNetAddConnection2.我更喜欢后者,因为我有时需要为不同的位置维护多个凭据.我将它包装成IDisposable并调用WNetCancelConnection2以后删除信用(避免多个用户名错误):
using (new NetworkConnection(@"\\server\read", readCredentials)) using (new NetworkConnection(@"\\server2\write", writeCredentials)) { File.Copy(@"\\server\read\file", @"\\server2\write\file"); }
7年后的今天,我面临同样的问题,我想分享我的解决方案版本.
它是复制和粘贴准备:-)这里是:
步骤1在您的代码中(每当您需要使用权限执行某些操作时)
ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate { //Your code here //Let's say file copy: if (!File.Exists(to)) { File.Copy(from, to); } });第2步
帮助文件做了一个魔术
using System; using System.Runtime.ConstrainedExecution; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.Security.Principal; using Microsoft.Win32.SafeHandles; namespace BlaBla { public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid { private SafeTokenHandle() : base(true) { } [DllImport("kernel32.dll")] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [SuppressUnmanagedCodeSecurity] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle); protected override bool ReleaseHandle() { return CloseHandle(handle); } } public class ImpersonationHelper { [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] private extern static bool CloseHandle(IntPtr handle); [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute) { SafeTokenHandle safeTokenHandle; try { const int LOGON32_PROVIDER_DEFAULT = 0; //This parameter causes LogonUser to create a primary token. const int LOGON32_LOGON_INTERACTIVE = 2; // Call LogonUser to obtain a handle to an access token. bool returnValue = LogonUser(userName, domainName, userPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle); //Facade.Instance.Trace("LogonUser called."); if (returnValue == false) { int ret = Marshal.GetLastWin32Error(); //Facade.Instance.Trace($"LogonUser failed with error code : {ret}"); throw new System.ComponentModel.Win32Exception(ret); } using (safeTokenHandle) { //Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}"); //Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}"); // Use the token handle returned by LogonUser. using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())) { using (WindowsImpersonationContext impersonatedUser = newId.Impersonate()) { //Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}"); //Facade.Instance.Trace("Start executing an action"); actionToExecute(); //Facade.Instance.Trace("Finished executing an action"); } } //Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}"); } } catch (Exception ex) { //Facade.Instance.Trace("Oh no! Impersonate method failed."); //ex.HandleException(); //On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/ throw; } } } }
我搜索了很多方法,我按照自己的方式做了.您必须通过命令提示符NET USE命令打开两台机器之间的连接,并在完成工作后清除与命令提示符NET USE"myconnection"/ delete的连接.
您必须使用代码后面的命令提示符进程,如下所示:
var savePath = @"\\servername\foldername\myfilename.jpg"; var filePath = @"C:\\temp\myfileTosave.jpg";
用法很简单:
SaveACopyfileToServer(filePath, savePath);
这是功能:
using System.IO using System.Diagnostics; public static void SaveACopyfileToServer(string filePath, string savePath) { var directory = Path.GetDirectoryName(savePath).Trim(); var username = "loginusername"; var password = "loginpassword"; var filenameToSave = Path.GetFileName(savePath); if (!directory.EndsWith("\\")) filenameToSave = "\\" + filenameToSave; var command = "NET USE " + directory + " /delete"; ExecuteCommand(command, 5000); command = "NET USE " + directory + " /user:" + username + " " + password; ExecuteCommand(command, 5000); command = " copy \"" + filePath + "\" \"" + directory + filenameToSave + "\""; ExecuteCommand(command, 5000); command = "NET USE " + directory + " /delete"; ExecuteCommand(command, 5000); }
并且ExecuteCommand函数是:
public static int ExecuteCommand(string command, int timeout) { var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command) { CreateNoWindow = true, UseShellExecute = false, WorkingDirectory = "C:\\", }; var process = Process.Start(processInfo); process.WaitForExit(timeout); var exitCode = process.ExitCode; process.Close(); return exitCode; }
这个功能对我来说非常快速和稳定.
Luke Quinane解决方案看起来不错,但在我的ASP.NET MVC应用程序中只能部分工作.在具有不同凭据的同一服务器上拥有两个共享,我可以仅对第一个使用模拟.
WNetAddConnection2的问题还在于它在不同的Windows版本上表现不同.这就是我寻找替代品并找到LogonUser功能的原因.这是我的代码,也适用于ASP.NET:
public sealed class WrappedImpersonationContext { public enum LogonType : int { Interactive = 2, Network = 3, Batch = 4, Service = 5, Unlock = 7, NetworkClearText = 8, NewCredentials = 9 } public enum LogonProvider : int { Default = 0, // LOGON32_PROVIDER_DEFAULT WinNT35 = 1, WinNT40 = 2, // Use the NTLM logon provider. WinNT50 = 3 // Use the negotiate logon provider. } [DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)] public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken); [DllImport("kernel32.dll")] public extern static bool CloseHandle(IntPtr handle); private string _domain, _password, _username; private IntPtr _token; private WindowsImpersonationContext _context; private bool IsInContext { get { return _context != null; } } public WrappedImpersonationContext(string domain, string username, string password) { _domain = String.IsNullOrEmpty(domain) ? "." : domain; _username = username; _password = password; } // Changes the Windows identity of this thread. Make sure to always call Leave() at the end. [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public void Enter() { if (IsInContext) return; _token = IntPtr.Zero; bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token); if (!logonSuccessfull) { throw new Win32Exception(Marshal.GetLastWin32Error()); } WindowsIdentity identity = new WindowsIdentity(_token); _context = identity.Impersonate(); Debug.WriteLine(WindowsIdentity.GetCurrent().Name); } [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")] public void Leave() { if (!IsInContext) return; _context.Undo(); if (_token != IntPtr.Zero) { CloseHandle(_token); } _context = null; } }
用法:
var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password); impersonationContext.Enter(); //do your stuff here impersonationContext.Leave();
对于VB.lovers来说,VB.NET相当于Luke Quinane的代码(感谢Luke!)。
Imports System Imports System.Net Imports System.Runtime.InteropServices Imports System.ComponentModel Public Class NetworkConnection Implements IDisposable Private _networkName As String Public Sub New(networkName As String, credentials As NetworkCredential) _networkName = networkName Dim netResource = New NetResource() With { .Scope = ResourceScope.GlobalNetwork, .ResourceType = ResourceType.Disk, .DisplayType = ResourceDisplaytype.Share, .RemoteName = networkName } Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName)) Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0) If result <> 0 Then Throw New Win32Exception(result, "Error connecting to remote share") End If End Sub Protected Overrides Sub Finalize() Try Dispose (False) Finally MyBase.Finalize() End Try End Sub Public Sub Dispose() Implements IDisposable.Dispose Dispose (True) GC.SuppressFinalize (Me) End Sub Protected Overridable Sub Dispose(disposing As Boolean) WNetCancelConnection2(_networkName, 0, True) End Sub_ Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer End Function _ Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer End Function End Class _ Public Class NetResource Public Scope As ResourceScope Public ResourceType As ResourceType Public DisplayType As ResourceDisplaytype Public Usage As Integer Public LocalName As String Public RemoteName As String Public Comment As String Public Provider As String End Class Public Enum ResourceScope As Integer Connected = 1 GlobalNetwork Remembered Recent Context End Enum Public Enum ResourceType As Integer Any = 0 Disk = 1 Print = 2 Reserved = 8 End Enum Public Enum ResourceDisplaytype As Integer Generic = &H0 Domain = &H1 Server = &H2 Share = &H3 File = &H4 Group = &H5 Network = &H6 Root = &H7 Shareadmin = &H8 Directory = &H9 Tree = &HA Ndscontainer = &HB End Enum