Как указать имя пользователя и пароль при подключении к сетевому ресурсу
при подключении к сетевому ресурсу, для которого текущий пользователь (в моем случае пользователь службы с поддержкой сети) не имеет прав, необходимо указать имя и пароль.
Я знаю, как это сделать с функциями Win32 (the WNet* семья mpr.dll), но хотел бы сделать это с помощью функциональности .Net (2.0).
какие варианты доступны?
может быть, еще какая-то информация поможет:
- вариант использования-это windows сервис, а не ан Asp.Net применение.
- служба работает под учетной записью, которая не имеет прав на общий ресурс.
- учетная запись пользователя, необходимая для общего ресурса, не известна на стороне клиента.
- клиент и сервер не являются членами одного домена.
11 ответов:
вы можете либо изменить идентификатор потока, либо P/Invoke WNetAddConnection2. Я предпочитаю последнее, так как мне иногда нужно поддерживать несколько учетных данных для разных мест. Я обертываю его в IDisposable и вызываю WNetCancelConnection2, чтобы впоследствии удалить creds (избегая ошибки с несколькими именами пользователей):
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); } } ~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 }
сегодня, 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; }эта функция работает очень быстро и стабильно для меня.
решение Люка Хинана выглядит хорошо, но работало только частично в моем ASP.NET приложение MVC. Имея два общих ресурса на одном сервере с разными учетными данными, я мог использовать олицетворение только для первого.
проблема с WNetAddConnection2 также заключается в том, что он ведет себя по-разному в разных версиях windows. Вот почему я искал альтернативы и нашел LogonUser
один из вариантов, который может работать с помощью
WindowsIdentity.Impersonate(и изменить принцип потока), чтобы стать желаемым пользователем,вот так. Однако, боюсь, вернемся к p/invoke...другой дерзкий (и столь же далекий от идеала) вариант может заключаться в том, чтобы породить процесс для выполнения работы...
ProcessStartInfoпринимает a.UserName,.Passwordи.Domain.
наконец-возможно, запустить службу в выделенной учетной записи, которая имеет доступ?(удалить, как вы уточнили, что это это не вариант).
ОК... Я могу резонировать..
отказ от ответственности: у меня только что был 18+ часовой день (снова).. Я стар и забывчив.. Я не умею писать.. У меня короткий промежуток внимания, поэтому я лучше отвечаю быстро.. : -)
вопрос:
можно ли изменить участника потока на пользователя без учетной записи на локальном компьютере?
ответ:
Да, вы можете изменить участника потока, даже если учетные данные, которые вы используете, не определены локально или находятся вне лес."
Я просто столкнулся с этой проблемой при попытке подключиться к SQL server с проверкой подлинности NTLM из службы. Этот вызов использует учетные данные, связанные с процессом, что означает, что вам нужна локальная учетная запись или учетная запись домена для проверки подлинности, прежде чем вы сможете олицетворять. Бла-бла...
но...
Вызов LogonUser(..) с атрибутом ????_NEW_CREDENTIALS вернет маркер безопасности без попытки проверки подлинности полномочия. Кульно.. Не нужно определять учетную запись в "лесу". Если у вас есть токен, вам может потребоваться вызвать DuplicateToken() с возможностью включить олицетворение, что приведет к созданию нового токена. Теперь вызовите SetThreadToken (NULL, token); (это может быть &token?).. Вызов ImpersonateLoggedonUser (token ); может потребоваться, но я так не думаю. Посмотреть его..
делать то, что вам нужно сделать..
вызов RevertToSelf() если вы вызвали ImpersonateLoggedonUser () то SetThreadToken (NULL, NULL); (я думаю... посмотрите его), а затем CloseHandle() на созданных дескрипторах..
никаких обещаний, но это сработало для меня... Это с моей головы (как и мои волосы), и я не могу писать!!!
для VB.влюбленные the VB.NET эквивалент кода Люка Квинана (спасибо Люк!)
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
вы должны смотреть на добавление, как такой:
<identity impersonate="true" userName="domain\user" password="****" />в свою паутину.конфиг.
Если вы не можете создать локально допустимый маркер безопасности, похоже, что вы исключили все опции Win32 API и WNetAddConnection*.
тонны информации о MSDN о Wnet-PInvoke информации и пример кода, который подключается к UNC-пути здесь:
http://www.pinvoke.net/default.aspx/mpr/WNetAddConnection2.html#
ссылка MSDN здесь:
http://msdn.microsoft.com/en-us/library/aa385391 (VS. 85). aspx
портирован на F# использовать подделка
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
Comments