Jak podać nazwę użytkownika i hasło podczas łączenia się z udziałem sieciowym

191

Podczas łączenia się z udziałem sieciowym, dla którego bieżący użytkownik (w moim przypadku użytkownik usług sieciowych) nie ma żadnych praw, należy podać nazwę i hasło.

Wiem jak to zrobić z funkcjami Win32 ( WNet*rodzina od mpr.dll), ale chciałbym to zrobić z funkcjonalnością .Net (2.0).

Jakie opcje są dostępne?

Może więcej informacji pomaga:

  • Przypadek użycia to usługa systemu Windows, a nie aplikacja Asp.Net.
  • Usługa działa na koncie, które nie ma uprawnień do udziału.
  • Konto użytkownika potrzebne do udziału nie jest znane po stronie klienta.
  • Klient i serwer nie są członkami tej samej domeny.
gyrolf
źródło
7
Chociaż nie udzielam użytecznej odpowiedzi, mogę udzielić odpowiedzi przeciwnej. Podszywanie się pod inne osoby i odradzanie procesu zgodnie z założeniami Marc nie będzie działać, gdy serwer i klient nie są w tej samej domenie, chyba że istnieje zaufanie między dwie domeny. Jeśli istnieje zaufanie, myślę, że to zadziała. Chciałbym tylko odpowiedzieć jako komentarz do Marca, ale nie mam wystarczającej liczby przedstawicieli, aby skomentować. : - /
Moose

Odpowiedzi:

152

Możesz zmienić tożsamość wątku lub P / Invoke WNetAddConnection2. Wolę to drugie, ponieważ czasami muszę zachować wiele poświadczeń dla różnych lokalizacji. Zawijam go w IDisposable i wywołuję WNetCancelConnection2, aby później usunąć kredyty (unikając błędu wielu nazw użytkowników):

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}
Mark Brackett
źródło
4
Usługa nie należy do domeny docelowej - personifikacja nie może działać, ponieważ nie można utworzyć lokalnego tokenu zabezpieczającego i podszyć się pod niego. PInvoke jest jedynym sposób.
stephbu,
@MarkBrackett Wiem, że to stara odpowiedź, ale może nadal wiesz ... czy dostęp zostanie przyznany tylko programowi, czy też zalogowanemu użytkownikowi za pośrednictwem Eksploratora?
Bryza
@Breeze - Nie testowałem tego, ale spodziewam się, że uwierzytelnienie nastąpi podczas sesji logowania; więc jeśli twój program działa jako zalogowany użytkownik, miałby również dostęp (przynajmniej przez czas trwania operacji).
Mark Brackett
8
Definicje readCredentials i writeCredentials mogą być zawarte w odpowiedzi.
Anders Lindén
2
Jeśli pojawia się błąd 53 , upewnij się, że ścieżka nie kończy się na „\”
Mustafa S.
326

Tak bardzo podobała mi się odpowiedź Marka Bracketta , że zrobiłem własną szybką implementację. Oto, jeśli ktoś w pośpiechu tego potrzebuje:

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
}
Luke Quinane
źródło
10
Naprawdę powinno być throw new Win32Exception(result);, ponieważ WNetAddConnection2 zwraca kody błędów win32 ( ERROR_XXX)
torvin
2
To jest świetny mały kawałek kodu. Konieczne było zalogowanie się do systemu UNIX, aby uzyskać listę katalogów do drukowania w aplikacji internetowej MVC5, i to załatwiło sprawę. +1 !!!
Tay
3
Aby powyższy kod został skompilowany, wymagane są następujące instrukcje użycia: using System.Net; using System.Runtime.InteropServices; using System.ComponentModel;
Matt Nelson
4
przepraszam za odświeżenie tego starego wątku, ale wygląda na to, że nie zamyka on połączenia po zakończeniu bloku. Mam program do wgrywania kilku zdjęć, pierwsze idzie dobrze, drugie daje błąd. Połączenie zostaje zwolnione po zamknięciu programu. Jakaś rada?
arti
3
Mieliśmy ten sam problem co ty, @arti. Wystarczy ustawić nazwę użytkownika i hasło do NetworkCredentialobiektu, aby aplikacja mogła połączyć się raz z dyskiem sieciowym. Następnie otrzymywaliśmy ERROR_LOGON_FAILURE przy każdej próbie do momentu ponownego uruchomienia aplikacji. Próbowaliśmy następnie podać domenę również na NetworkCredentialobiekcie i nagle zadziałało! Nie mam pojęcia, dlaczego to rozwiązało problem, zwłaszcza fakt, że udało się połączyć raz bez domeny.
lsmeby,
50

Dzisiaj 7 lat później mam do czynienia z tym samym problemem i chciałbym udostępnić moją wersję rozwiązania.

Jest gotowy do kopiowania i wklejania :-) Oto on:

Krok 1

W twoim kodzie (ilekroć musisz zrobić coś z uprawnieniami)

ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
                            {
                                //Your code here 
                                //Let's say file copy:
                                if (!File.Exists(to))
                                {
                                    File.Copy(from, to);
                                }
                            });

Krok 2

Plik pomocnika, który robi magię

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;
            }
        }
    }
}
Pavel Kovalev
źródło
2
@MohammadRashid Zgodnie z dokumentacją dotyczącą LogonUser działa tylko dla użytkowników na komputerze lokalnym: „Funkcja LogonUser próbuje zalogować użytkownika na komputerze lokalnym. Komputer lokalny to komputer, z którego wywołano LogonUser. Nie można użyć LogonUser aby zalogować się do komputera zdalnego. ”Pojawi się błąd„ Wyjątek Win32: Nazwa użytkownika lub hasło są niepoprawne ”. Więc przypuszczam, że maszyny muszą być przynajmniej w tej samej domenie.
Charles Chen,
1
@CharlesChen Właśnie udowodniono, że działa dobrze w różnych domenach, FYI. Serwer, na którym działam, znajduje się w strefie DMZ i zdecydowanie łączy się z serwerem plików w innej domenie za pośrednictwem zapory. Fragment zabójcy Pavel, to ty jesteś mężczyzną i to prawdopodobnie powinna być dzisiaj odpowiedź zaakceptowana.
Brian MacKay
To jest świetne rozwiązanie! Dziękuję, Pavel Kovalev.
STLDev
czy to działa na ldap? mówi, że nie mam dostępnego serwera logowania. używam ldap auth
Julius Limson
28

Przeszukałem wiele metod i zrobiłem to po swojemu. Musisz otworzyć połączenie między dwoma urządzeniami za pomocą wiersza polecenia polecenie NET USE, a po zakończeniu pracy wyczyść połączenie za pomocą wiersza polecenia NET USE „myconnection” / delete.

Musisz użyć procesu wiersza polecenia z tyłu kodu w następujący sposób:

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

Użycie jest proste:

SaveACopyfileToServer(filePath, savePath);

Oto funkcje:

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

A także funkcja ExecuteCommand to:

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

Ta funkcja działała dla mnie bardzo szybko i stabilnie.

Hakan KOSE
źródło
1
W przypadku niepowodzenia mapowania udziału, jakie byłyby kody powrotu?
surega
14

Rozwiązanie Luke Quinane wygląda dobrze, ale działało tylko częściowo w mojej aplikacji ASP.NET MVC. Mając dwa udziały na tym samym serwerze z różnymi danymi uwierzytelniającymi, mogłem użyć personifikacji tylko dla pierwszego.

Problem z WNetAddConnection2 polega również na tym, że zachowuje się inaczej w różnych wersjach systemu Windows. Dlatego szukałem alternatyw i znalazłem funkcję LogonUser . Oto mój kod, który działa również w 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;
    }
}

Stosowanie:

var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
impersonationContext.Enter();

//do your stuff here

impersonationContext.Leave();
VladL
źródło
2
takie podejście działało dla mnie dobrze, ale zauważyłem w moich testach, że gdy używasz złego hasła do konta użytkownika domeny, ten użytkownik natychmiast przechodzi w stan zablokowania. nasze zasady domeny wymagają 3 nieudanych prób logowania, zanim to nastąpi, ale dzięki temu podejściu jedna zła próba i jesteś zablokowany. Dlatego używaj ostrożnie ...
kellyb
5

Dla VB.lovers odpowiednik VB.NET kodu Luke'a Quinane'a (dzięki 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
Alessandro Bernardi
źródło
3

Jedną z opcji, że praca może jest za pomocą WindowsIdentity.Impersonate(i zmienić głównego wątku), aby stać się żądany użytkownika, jak tak . Wróćmy jednak do p / invoke, obawiam się ...

Inną bezczelną (i równie daleką od ideału) opcją może być odrodzenie procesu wykonywania pracy ... ProcessStartInfoakceptuje .UserName, .Passwordi .Domain.

Wreszcie - może uruchomić usługę na dedykowanym koncie, które ma dostęp? (usunięty, ponieważ wyjaśniłeś, że nie jest to opcja).

Marc Gravell
źródło
nie sądzę, że proces jest tak złym pomysłem. Google opublikowało kilka oficjalnych dokumentów na temat korzyści wynikających z wieloprocesowego przetwarzania w chrome.
Dustin Getz
Czy można zmienić nazwę użytkownika wątku na użytkownika bez konta na komputerze lokalnym?
gyrolf,
Szczerze mówiąc, po prostu nie wiem ... Aby się dowiedzieć, musiałbyś wypróbować LogonUser z inną domeną.
Marc Gravell
3

OK ... mogę ponownie odpowiedzieć ..

Oświadczenie: Właśnie miałem ponad 18 godzinny dzień (znowu) .. Jestem stary i zapominam .. Nie mogę przeliterować .. Mam krótki okres uwagi, więc lepiej odpowiedzieć szybko .. :-)

Pytanie:

Czy można zmienić nazwę użytkownika wątku na użytkownika bez konta na komputerze lokalnym?

Odpowiedź:

Tak, możesz zmienić nazwę wątku, nawet jeśli używane poświadczenia nie są zdefiniowane lokalnie lub znajdują się poza „lasem”.

Właśnie natknąłem się na ten problem podczas próby połączenia się z serwerem SQL z uwierzytelnianiem NTLM z usługi. To połączenie wykorzystuje dane uwierzytelniające powiązane z procesem, co oznacza, że ​​do uwierzytelnienia się potrzebujesz konta lokalnego lub konta domeny. Bla bla...

Ale...

Wywołanie LogonUser (..) z atrybutem ???? _ NEW_CREDENTIALS zwróci token zabezpieczający bez próby uwierzytelnienia poświadczeń. Kewl .. Nie musisz definiować konta w „lesie”. Po uzyskaniu tokena może być konieczne wywołanie DuplicateToken () z opcją włączenia personifikacji, co powoduje powstanie nowego tokena. Teraz wywołaj SetThreadToken (NULL, token); (Może to być & token?) .. Wywołanie do ImpersonateLoggedonUser (token); może być wymagane, ale nie sądzę. Sprawdź to ...

Rób, co musisz zrobić ...

Wywołaj RevertToSelf (), jeśli wywołałeś ImpersonateLoggedonUser (), a następnie SetThreadToken (NULL, NULL); (Myślę ... spójrz w górę), a następnie CloseHandle () na utworzonych uchwytach ..

Żadnych obietnic, ale to zadziałało dla mnie ... To jest poza moją głową (jak moje włosy) i nie mogę przeliterować !!!

Craig Armstrong
źródło
1

Przeniesiony również na F # do użycia z 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
python_kaa
źródło
0

Powinieneś rozważyć dodanie czegoś takiego:

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

W twój web.config.

Więcej informacji.

GEOCHET
źródło
Niektóre zabezpieczenia korporacyjne uniemożliwiają podszywanie się pod inne osoby, ponieważ nie są w stanie śledzić aplikacji, która je używa i muszą należeć do tej samej lub zaufanej domeny. Myślę, że zauważono podszywanie się pod wsparcie. Najlepszym rozwiązaniem wydaje się konto usługi domenowej z pinvoke.
Jim