Как указать имя пользователя и пароль при подключении к сетевому ресурсу

При подключении к общему сетевому ресурсу, для которого текущий пользователь (в моем случае пользователь службы с включенной сетью) не имеет прав, необходимо указать имя и пароль.

Я знаю, как сделать это с функциями Win32 (из WNet*семейства mpr.dll), но хотел бы сделать это с функциональностью .Net (2.0).

Какие варианты доступны?

Может быть, поможет дополнительная информация:

  • Вариант использования - это служба Windows, а не приложение Asp.Net.
  • Служба работает под учетной записью, которая не имеет прав на общий ресурс.
  • Учетная запись пользователя, необходимая для общего ресурса, неизвестна на стороне клиента.
  • Клиент и сервер не являются членами одного домена.
17.11.2008 13:07:44
Хотя я не даю вам полезного ответа, я могу предоставить анти-ответ. Олицетворение и порождение процесса, как предположил Марк, не будут работать, когда сервер и клиент не находятся в одном домене, если нет доверия между два домена. Если есть доверие, то я думаю, что оно будет работать. Я бы просто ответил как комментарий к Марку, но мне не хватает представителя, чтобы комментировать. : - /
Moose 17.11.2008 14:23:38
vapcguy 24.01.2017 16:38:57
11 ОТВЕТОВ
РЕШЕНИЕ

Вы можете либо изменить идентификатор потока, либо 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");
}
151
17.11.2008 14:39:18
Служба не является членом целевого домена - олицетворение не может работать, поскольку вы не сможете создать токен безопасности локально и имитировать его. PInvoke - единственный способ.
stephbu 17.11.2008 17:31:53
@MarkBrackett Я знаю, что это старый ответ, но, может быть, вы все еще знаете ... Будет ли доступ предоставлен только программе или также зарегистрированному пользователю через проводник?
Breeze 21.03.2016 10:06:10
@ Ветерок - я не проверял это, но я ожидал бы, что это аутентифицирует для сеанса входа в систему; так что если ваша программа работает как вошедший в систему пользователь, у них также будет доступ (по крайней мере, на время операции).
Mark Brackett 21.03.2016 15:45:25
@MarkBrackett Я тем временем проверил это, и ваше ожидание было верным.
Breeze 22.03.2016 08:18:55
Определения readCredentials и writeCredentials могут быть включены в ответ.
Anders Lindén 3.05.2017 09:34:32

Вы должны смотреть на добавление как это:

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

В ваш web.config.

Дополнительная информация.

0
17.11.2008 13:12:23
Некоторые средства корпоративной безопасности запрещают использование олицетворения, поскольку они не могут отслеживать приложение, использующее его, и должны находиться в том же или доверенном домене. Я думаю, что подражание поддержки замечено. Учетная запись службы домена с pinvoke, кажется, путь.
Jim 19.02.2019 04:13:48

Один из вариантов, который может работать, - это использовать WindowsIdentity.Impersonate(и изменить принцип потока), чтобы стать желаемым пользователем, вот так . Вернуться к п / я вызываю, хотя, боюсь ...

Другой вариант дерзкий (и в равной степени далека от идеала) , может быть , чтобы породить процесс , чтобы сделать работу ... ProcessStartInfoпринимает .UserName, .Passwordи .Domain.

Наконец - возможно, запустить службу в выделенной учетной записи, которая имеет доступ? (удалено, поскольку вы пояснили, что это не вариант).

3
17.11.2008 13:23:11
я не думаю, что процесс это такая плохая идея. Google выпустил несколько статей о преимуществах многопроцессорности в Chrome.
Dustin Getz 17.11.2008 13:26:03
Можно ли сменить субъект потока на пользователя без учетной записи на локальном компьютере?
gyrolf 17.11.2008 13:52:39
Если честно, я просто не знаю ... Вы должны попробовать LogonUser с другим доменом, чтобы узнать.
Marc Gravell♦ 17.11.2008 14:00:50

Если вы не можете создать локально действующий токен безопасности, похоже, что вы исключили все панели параметров 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

1
17.11.2008 14:35:27

Мне так понравился ответ Марка Брэкетта, что я сделал свою собственную быструю реализацию. Вот если кто-то спешит:

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
}
317
26.09.2018 12:09:02
Так и должно быть throw new Win32Exception(result);, поскольку WNetAddConnection2 возвращает коды ошибок win32 ( ERROR_XXX)
torvin 16.05.2011 08:22:27
Это блестящий маленький кусочек кода. Необходим вход в систему UNIX для получения списка каталогов для печати в веб-приложении MVC5, и это помогло. +1 !!!
Tay 27.01.2014 09:07:54
Для компиляции приведенного выше кода требуются следующие операторы использования: using System.Net; using System.Runtime.InteropServices; using System.ComponentModel;
Matt Nelson 17.12.2015 17:00:01
Извините, что обновил этот старый поток, но похоже, что он не закрывает соединение после завершения блока. У меня есть программа для загрузки нескольких картинок, первая идет нормально, вторая дает сбой. Соединение освобождается, когда программа закрыта. Любой совет?
arti 24.12.2015 08:52:05
У нас была такая же проблема, как и у тебя, @arti. Просто установив имя пользователя и пароль на NetworkCredentialобъекте, приложение могло подключиться к сетевому диску один раз. После этого мы получали ERROR_LOGON_FAILURE при каждой попытке, пока приложение не было перезапущено. Затем мы попытались предоставить домен и для NetworkCredentialобъекта, и вдруг это сработало! Я понятия не имею, почему это решило проблему, особенно тот факт, что он работал, чтобы подключиться один раз без домена.
lsmeby 18.08.2016 13:04:33

ОК ... Я могу ответить ..

Отказ от ответственности: у меня только что был 18+ часовой день (снова) .. Я стар и забыл ... Я не могу произнести по буквам ... У меня короткая продолжительность концентрации внимания, поэтому я лучше отвечаю быстро .. :-)

Вопрос:

Можно ли сменить субъект потока на пользователя без учетной записи на локальном компьютере?

Ответ:

Да, вы можете изменить принципала потока, даже если учетные данные, которые вы используете, не определены локально или находятся вне «леса».

Я только столкнулся с этой проблемой при попытке подключиться к серверу SQL с проверкой подлинности NTLM из службы. В этом вызове используются учетные данные, связанные с процессом, а это означает, что для аутентификации вам потребуется либо локальная учетная запись, либо учетная запись домена. Бла, бла ...

Но...

Вызов LogonUser (..) с атрибутом ???? _ NEW_CREDENTIALS вернет маркер безопасности без попытки аутентификации учетных данных. Kewl .. Не нужно определять учетную запись в «лесу». Когда у вас есть токен, вам, возможно, придется вызвать DuplicateToken () с возможностью включить олицетворение, в результате которого будет получен новый токен. Теперь вызовите SetThreadToken (NULL, токен); (Это может быть & token?) .. Вызов ImpersonateLoggedonUser (token); может потребоваться, но я так не думаю. Поищи это..

Делай то, что нужно сделать ..

Вызовите RevertToSelf (), если вы вызвали ImpersonateLoggedonUser (), затем SetThreadToken (NULL, NULL); (Я думаю ... посмотрите), а затем CloseHandle () на созданные дескрипторы ..

Никаких обещаний, но это сработало для меня ... Это на макушке моей головы (как мои волосы), и я не могу по буквам !!!

3
30.01.2010 08:12:10

Я искал много методов, и я сделал это по-своему. Вы должны открыть соединение между двумя компьютерами с помощью командной строки 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;
    } 

Эти функции работали очень быстро и стабильно для меня.

28
17.07.2013 13:09:32
В случае сбоя сопоставления общего ресурса, какими будут коды возврата?
surega 23.10.2018 09:05:55

Решение 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();
14
18.03.2014 14:49:13
этот подход хорошо работал для меня, но в моем тестировании я заметил, что при использовании неверного пароля с учетной записью пользователя домена этот пользователь немедленно переходит в заблокированное состояние. наша политика домена требует 3 неудачных попыток входа в систему до того, как это произойдет, но при таком подходе одна неудачная попытка - и вы заблокированы. Так что, используйте с осторожностью ...
kellyb 2.10.2015 17:46:53

Для VB.lovers 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
5
5.11.2015 15:42:15

Также портирован на 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
1
10.11.2015 16:22:04

Сегодня, 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;
            }
        }
    }
}
50
16.09.2016 21:39:44
@MohammadRashid Согласно документации на LogonUser , он работает только для пользователей на локальном компьютере: «Функция LogonUser пытается войти в систему пользователя на локальном компьютере. Локальный компьютер - это компьютер, с которого был вызван LogonUser. Вы не можете использовать LogonUser войти в систему на удаленном компьютере. «Вы получите сообщение об ошибке« Win32Exception: имя пользователя или пароль неверны ». Поэтому я полагаю, что машины должны быть в одном домене, по крайней мере.
Charles Chen 9.10.2017 17:19:04
@CharlesChen Только что доказал, что это прекрасно работает в разных областях, к вашему сведению. Сервер, на котором я работаю, находится в демилитаризованной зоне и определенно подключается к файловому серверу в другом домене через брандмауэр. Фрагмент убийцы Павел, ты - мужчина, и сегодня, наверное, это должен быть принятый ответ.
Brian MacKay 26.10.2017 13:09:12
Это ОТЛИЧНОЕ РЕШЕНИЕ! Спасибо, Павел Ковалев.
STLDev 27.09.2019 21:39:18
это работает на ldap? он говорит, что у меня нет сервера входа в систему. я использую ldap auth
Julius Limson 11.11.2019 08:09:52