连接到networking共享时如何提供用户名和密码

当连接到当前用户(在我的情况下,networking使能的服务用户)没有权限的networking共享时,必须提供名称和密码。

我知道如何使用Win32函数(来自mpr.dllWNet*系列)执行此操作,但希望使用.Net(2.0)function执行此操作。

有什么select可用?

也许更多的信息可以帮助:

  • 用例是一个Windows服务,而不是一个Asp.Net应用程序。
  • 该服务运行在一个没有共享权限的帐户下。
  • 客户端不知道共享所需的用户帐户。
  • 客户端和服务器不是同一个域的成员。

您可以更改线程标识,或者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"); } 

我非常喜欢马克·布拉克特的回答,所以我做了我自己的快速实施。 在这里,如果有其他人急需它,

 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, "Error connecting to remote share"); } } ~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 } 

我search了很多方法,我自己做了。 您必须通过命令提示符NET USE命令打开两台机器之间的连接,并在完成您的工作后,使用命令提示符清除连接NET USE“myconnection”/ delete。

您必须使用后面的代码的命令提示过程,如下所示:

 var savePath = @"\\servername\foldername\myfilename.jpg"; var filePath = @"C:\\temp\myfileTosave.jpg"; 

用法很简单:

 SaveACopyfileToServer(filePath, savePath); 

这里是function:

 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的function是:

 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; } 

这个function对我来说非常快速和稳定。

今天7年后,我面临同样的问题,我想分享我的版本的解决scheme。

这是复制和粘贴准备:-)这里是:

步骤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; } } } } 

Luke Quinane解决scheme看起来不错,但是在我的ASP.NET MVC应用程序中只做了部分工作。 在具有不同凭据的同一台服务器上有两个共享,我只能使用第一个模拟。

WNetAddConnection2的问题也是在不同的Windows版本上performance不同。 这就是为什么我寻找替代品,并findLogonUserfunction。 这是我的代码,它也适用于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(); 

可能有效的一个select是使用WindowsIdentity.Impersonate (并更改线程主体)以成为所需的用户, 就像这样 。 回到p / invoke,但是,恐怕…

另一个厚颜无耻的(也是相当不理想的)选项可能是产生一个进程来完成工作… ProcessStartInfo接受.UserName.Password.Domain

最后 – 也许运行服务在一个专门的帐户有访问权限? (删除,因为你已经澄清,这不是一个选项)。

好的…我可以重新..

免责声明:我只有一个18+小时的一天(再次)..我老了,忘记了..我不能拼写..我有一个短暂的关注跨度,所以我更好地回应快::-)

题:

是否有可能更改线程主体为本地计算机上没有帐户的用户?

回答:

是的,即使您使用的凭证未在本地定义或在“森林”之外,也可以更改线程主体。

我试图用服务连接到NTLM身份validation的SQL服务器时遇到了这个问题。 此调用使用与该进程关联的凭据,这意味着在模拟之前需要本地帐户或域帐户进行身份validation。 Blah,blah …

但…

使用???? _ NEW_CREDENTIALS属性调用LogonUser(..)将返回一个安全令牌,而不会尝试对凭证进行身份validation。 Kewl ..不必在“森林”内定义帐户。 一旦你有了令牌,你可能需要调用DuplicateToken()来启用模拟,从而产生一个新的令牌。 现在调用SetThreadToken(NULL,token); (它可能是&token?)。调用ImpersonateLoggedonUser(token); 可能是必需的,但我不这么认为。 看起来

做你需要做的事情

如果调用ImpersonateLoggedonUser(),则调用RevertToSelf(),然后调用SetThreadToken(NULL,NULL); (我想…查找它),然后在创build的手柄上CloseHandle()。

没有承诺,但这对我来说…这是我的头顶(像我的头发),我不能拼!

你应该看看添加一个像这样的:

 <identity impersonate="true" userName="domain\user" password="****" /> 

进入你的web.config。

更多信息。

如果你不能创build一个本地有效的安全令牌,看起来你已经排除了所有的选项栏Win32 API和WNetAddConnection *。

关于WNet的MSDN信息 – PInvoke连接到UNCpath的信息和示例代码:

http://www.pinvoke.net/default.aspx/mpr/WNetAddConnection2.html#

这里的MSDN参考:

http://msdn.microsoft.com/en-us/library/aa385391(VS.85).aspx

对于VB.lovers Lucene Quinane代码的VB.NET等价物(感谢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 <DllImport("mpr.dll")> _ Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer End Function <DllImport("mpr.dll")> _ Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer End Function End Class <StructLayout(LayoutKind.Sequential)> _ 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 

也移植到F#与FAKE一起使用

 module NetworkShare open System open System.ComponentModel open System.IO open System.Net open System.Runtime.InteropServices type ResourceScope = | Connected = 1 | GlobalNetwork = 2 | Remembered = 3 | Recent = 4 type ResourceType = | Any = 0 | Disk = 1 | Print = 2 | Reserved = 8 type ResourceDisplayType = | Generic = 0x0 | Domain = 0x01 | Server = 0x02 | Share = 0x03 | File = 0x04 | Group = 0x05 | Network = 0x06 | Root = 0x07 | Shareadmin = 0x08 | Directory = 0x09 | Tree = 0x0a | Ndscontainer = 0x0b //Uses of this construct may result in the generation of unverifiable .NET IL code. #nowarn "9" [<StructLayout(LayoutKind.Sequential)>] type NetResource = struct val mutable Scope : ResourceScope val mutable ResourceType : ResourceType val mutable DisplayType : ResourceDisplayType val mutable Usage : int val mutable LocalName : string val mutable RemoteName : string val mutable Comment : string val mutable Provider : string new(name) = { // lets preset needed fields NetResource.Scope = ResourceScope.GlobalNetwork ResourceType = ResourceType.Disk DisplayType = ResourceDisplayType.Share Usage = 0 LocalName = null RemoteName = name Comment = null Provider = null } end type WNetConnection(networkName : string, credential : NetworkCredential) = [<Literal>] static let Mpr = "mpr.dll" [<DllImport(Mpr, EntryPoint = "WNetAddConnection2")>] static extern int connect(NetResource netResource, string password, string username, int flags) [<DllImport(Mpr, EntryPoint = "WNetCancelConnection2")>] static extern int disconnect(string name, int flags, bool force) let mutable disposed = false; do let userName = if String.IsNullOrWhiteSpace credential.Domain then credential.UserName else credential.Domain + "\\" + credential.UserName let resource = new NetResource(networkName) let result = connect(resource, credential.Password, userName, 0) if result <> 0 then let msg = "Error connecting to remote share " + networkName new Win32Exception(result, msg) |> raise let cleanup(disposing:bool) = if not disposed then disposed <- true if disposing then () // TODO dispose managed resources here disconnect(networkName, 0, true) |> ignore interface IDisposable with member __.Dispose() = disconnect(networkName, 0, true) |> ignore GC.SuppressFinalize(__) override __.Finalize() = cleanup(false) type CopyPath = | RemotePath of string * NetworkCredential | LocalPath of string let createDisposable() = { new IDisposable with member __.Dispose() = () } let copyFile overwrite destPath srcPath : unit = use _srcConn = match srcPath with | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable | LocalPath(_) -> createDisposable() use _destConn = match destPath with | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable | LocalPath(_) -> createDisposable() match srcPath, destPath with | RemotePath(src, _), RemotePath(dest, _) | LocalPath(src), RemotePath(dest, _) | RemotePath(src, _), LocalPath(dest) | LocalPath(src), LocalPath(dest) -> if FileInfo(src).Exists |> not then failwith ("Source file not found: " + src) let destFilePath = if DirectoryInfo(dest).Exists then Path.Combine(dest, Path.GetFileName src) else dest File.Copy(src, destFilePath, overwrite) let rec copyDir copySubDirs filePattern destPath srcPath = use _srcConn = match srcPath with | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable | LocalPath(_) -> createDisposable() use _destConn = match destPath with | RemotePath(path, credential) -> new WNetConnection(path, credential) :> IDisposable | LocalPath(_) -> createDisposable() match srcPath, destPath with | RemotePath(src, _), RemotePath(dest, _) | LocalPath(src), RemotePath(dest, _) | RemotePath(src, _), LocalPath(dest) | LocalPath(src), LocalPath(dest) -> let dir = DirectoryInfo(src) if dir.Exists |> not then failwith ("Source directory not found: " + src) let dirs = dir.GetDirectories() if Directory.Exists(dest) |> not then Directory.CreateDirectory(dest) |> ignore let files = dir.GetFiles(filePattern) for file in files do let tempPath = Path.Combine(dest, file.Name) file.CopyTo(tempPath, false) |> ignore if copySubDirs then for subdir in dirs do let subdirSrc = match srcPath with | RemotePath(_, credential) -> RemotePath(Path.Combine(dest, subdir.Name), credential) | LocalPath(_) -> LocalPath(Path.Combine(dest, subdir.Name)) let subdirDest = match destPath with | RemotePath(_, credential) -> RemotePath(subdir.FullName, credential) | LocalPath(_) -> LocalPath(subdir.FullName) copyDir copySubDirs filePattern subdirDest subdirSrc