So geben Sie Benutzername und Kennwort an, wenn Sie eine Verbindung zu einer Netzwerkfreigabe herstellen


191

Bei der Verbindung zu einer Netzwerkfreigabe, für die der aktuelle Benutzer (in meinem Fall ein netzwerkfähiger Dienstbenutzer) keine Rechte hat, müssen Name und Kennwort angegeben werden.

Ich weiß, wie man das mit Win32-Funktionen (der WNet*Familie von mpr.dll) macht, möchte es aber mit .Net (2.0) -Funktionalität machen.

Welche Optionen stehen zur Verfügung?

Vielleicht helfen noch ein paar Informationen:

  • Der Anwendungsfall ist ein Windows-Dienst, keine Asp.Net-Anwendung.
  • Der Dienst wird unter einem Konto ausgeführt, das keine Rechte an der Freigabe hat.
  • Das für die Freigabe erforderliche Benutzerkonto ist auf der Clientseite nicht bekannt.
  • Client und Server sind nicht Mitglieder derselben Domäne.

7
Obwohl ich Ihnen keine nützliche Antwort gebe, kann ich eine Anti-Antwort liefern. Der Identitätswechsel und das Laichen eines Prozesses, wie von Marc angegeben, funktionieren nicht, wenn sich der Server und der Client nicht in derselben Domäne befinden, es sei denn, es besteht ein Vertrauen zwischen ihnen die zwei Domänen. Wenn es ein Vertrauen gibt, denke ich, dass es funktionieren wird. Ich hätte nur als Kommentar auf Marc's geantwortet, aber ich habe nicht genug Repräsentanten, um einen Kommentar abzugeben. : - /
Elch

Antworten:


152

Sie können entweder die Thread-Identität ändern oder WNetAddConnection2 aufrufen. Ich bevorzuge letzteres, da ich manchmal mehrere Anmeldeinformationen für verschiedene Standorte verwalten muss. Ich verpacke es in ein IDisposable und rufe WNetCancelConnection2 auf, um die Creds anschließend zu entfernen (um den Fehler mit mehreren Benutzernamen zu vermeiden):

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}

4
Der Dienst ist kein Mitglied der Zieldomäne. Der Identitätswechsel kann nicht funktionieren, da Sie das Sicherheitstoken nicht lokal erstellen und sich als solcher ausgeben können. PInvoke ist der einzige Weg.
Stephbu

@MarkBrackett Ich weiß, dass dies eine alte Antwort ist, aber vielleicht wissen Sie immer noch ... wird der Zugriff nur auf das Programm oder auch auf den angemeldeten Benutzer über den Explorer gewährt?
Brise

@Breeze - Ich habe es nicht getestet, aber ich würde erwarten, dass es sich für die Anmeldesitzung authentifiziert. Wenn Ihr Programm also als angemeldeter Benutzer ausgeführt wird, hat dieser ebenfalls Zugriff (zumindest für die Dauer des Vorgangs).
Mark Brackett

8
Die Definitionen von readCredentials und writeCredentials könnten in der Antwort enthalten sein.
Anders Lindén

2
Wenn Sie Fehler 53 erhalten , stellen Sie sicher, dass der Pfad nicht mit einem "\" endet
Mustafa S.

326

Die Antwort von Mark Brackett hat mir so gut gefallen, dass ich meine eigene schnelle Implementierung durchgeführt habe. Hier ist es, wenn jemand anderes es in Eile braucht:

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
}

10
Es sollte wirklich sein throw new Win32Exception(result);, da WNetAddConnection2 win32-Fehlercodes ( ERROR_XXX)
zurückgibt

2
Dies ist ein brillanter kleiner Code. Musste mich bei einem UNIX-System anmelden, um eine Verzeichnisliste zum Drucken in einer MVC5-Webanwendung zu erhalten, und dies hat den Trick getan. +1 !!!
Tay

3
Die folgenden using-Anweisungen sind erforderlich, damit der obige Code kompiliert werden kann: using System.Net; using System.Runtime.InteropServices; using System.ComponentModel;
Matt Nelson

4
Es tut mir leid, diesen alten Thread zu aktualisieren, aber es sieht so aus, als würde die Verbindung nach Abschluss des Blocks nicht geschlossen. Ich habe ein Programm zum Hochladen einiger Bilder, das erste geht gut, das zweite gibt fehl. Die Verbindung wird freigegeben, wenn das Programm geschlossen wird. Irgendwelche Ratschläge?
Arti

3
Wir hatten das gleiche Problem wie Sie, @arti. Durch einfaches Festlegen des Benutzernamens und des Kennworts für das NetworkCredentialObjekt konnte die Anwendung einmal eine Verbindung zum Netzlaufwerk herstellen. Danach erhielten wir bei jedem Versuch eine ERROR_LOGON_FAILURE , bis die Anwendung neu gestartet wurde. Wir haben dann versucht, die Domain auch für das NetworkCredentialObjekt bereitzustellen, und plötzlich hat es funktioniert! Ich habe keine Ahnung, warum dies das Problem behoben hat, insbesondere die Tatsache, dass es funktioniert hat, eine Verbindung ohne die Domain einmal herzustellen.
lsmeby

50

Heute, 7 Jahre später, stehe ich vor dem gleichen Problem und möchte meine Version der Lösung teilen.

Es ist fertig zum Kopieren und Einfügen :-) Hier ist es:

Schritt 1

In Ihrem Code (wann immer Sie etwas mit Berechtigungen tun müssen)

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

Schritt 2

Die Helfer-Datei, die eine Magie macht

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

2
@MohammadRashid Gemäß der Dokumentation zu LogonUser funktioniert dies nur für Benutzer auf dem lokalen Computer: "Die LogonUser-Funktion versucht, einen Benutzer am lokalen Computer anzumelden. Der lokale Computer ist der Computer, von dem aus LogonUser aufgerufen wurde. Sie können LogonUser nicht verwenden um sich bei einem Remotecomputer anzumelden. "Sie erhalten eine Fehlermeldung" Win32Exception: Der Benutzername oder das Kennwort sind falsch. " Ich nehme an, die Maschinen müssen sich mindestens in derselben Domäne befinden.
Charles Chen

1
@CharlesChen Ich habe gerade bewiesen, dass dies domänenübergreifend gut funktioniert. Der Server, auf dem ich dies ausführe, befindet sich in einer DMZ und stellt über eine Firewall definitiv eine Verbindung zu einem Dateiserver in einer anderen Domäne her. Killerschnipsel Pavel, du bist der Mann, und dies sollte heute wahrscheinlich die akzeptierte Antwort sein.
Brian MacKay

Dies ist eine großartige Lösung! Vielen Dank, Pavel Kovalev.
STLDev

funktioniert das auf ldap? Es heißt, dass ich keinen Anmeldeserver zur Verfügung habe. Ich benutze ldap auth
Julius Limson

28

Ich habe viele Methoden gesucht und es auf meine eigene Weise gemacht. Sie müssen eine Verbindung zwischen zwei Computern über den Befehl NET USE der Eingabeaufforderung herstellen und nach Beendigung Ihrer Arbeit die Verbindung mit der Eingabeaufforderung NET USE "myconnection" / delete löschen.

Sie müssen den Eingabeaufforderungsprozess aus dem folgenden Code verwenden:

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

Die Verwendung ist einfach:

SaveACopyfileToServer(filePath, savePath);

Hier sind Funktionen:

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

Die ExecuteCommand-Funktion lautet außerdem:

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

Diese Funktionen haben bei mir sehr schnell und stabil funktioniert.


1
Wenn die Freigabezuordnung fehlschlägt, wie lauten die Rückkehrcodes?
Surega

14

Die Luke Quinane-Lösung sieht gut aus, hat aber in meiner ASP.NET MVC-Anwendung nur teilweise funktioniert. Mit zwei Freigaben auf demselben Server mit unterschiedlichen Anmeldeinformationen konnte ich den Identitätswechsel nur für die erste verwenden.

Das Problem mit WNetAddConnection2 ist auch, dass es sich unter verschiedenen Windows-Versionen unterschiedlich verhält. Deshalb habe ich nach Alternativen gesucht und die LogonUser- Funktion gefunden. Hier ist mein Code, der auch in ASP.NET funktioniert:

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

Verwendung:

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

//do your stuff here

impersonationContext.Leave();

2
Dieser Ansatz hat bei mir gut funktioniert, aber bei meinen Tests festgestellt, dass bei Verwendung eines falschen Kennworts mit einem Domänenbenutzerkonto dieser Benutzer sofort in den gesperrten Status versetzt wird. Unsere Domain-Richtlinie fordert 3 fehlgeschlagene Anmeldeversuche, bevor dies geschieht. Bei diesem Ansatz ist jedoch ein Fehlversuch fehlgeschlagen, und Sie sind gesperrt. Also mit Vorsicht verwenden ...
Kellyb

5

Für VB.lovers das VB.NET-Äquivalent von Luke Quinanes Code (danke 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

3

Eine Option, die möglicherweise funktioniert, ist die Verwendung WindowsIdentity.Impersonate(und Änderung des Thread-Prinzips), um der gewünschte Benutzer zu werden . Zurück zu p / invoke, fürchte ich ...

Eine andere freche (und ebenso weit vom Ideal entfernte) Option könnte darin bestehen, einen Prozess zu erzeugen, um die Arbeit zu erledigen ... ProcessStartInfoakzeptiert ein .UserName, .Passwordund.Domain .

Schließlich - vielleicht den Dienst in einem dedizierten Konto ausführen, das Zugriff hat? (entfernt, da Sie klargestellt haben, dass dies keine Option ist).


Ich denke nicht, dass der Prozess eine so schlechte Idee ist. Google hat einige Whitepapers über die Vorteile der Mehrfachverarbeitung in Chrom veröffentlicht.
Dustin Getz

Ist es möglich, das Thread-Prinzip in einen Benutzer ohne Konto auf dem lokalen Computer zu ändern?
Gyrolf

Um ehrlich zu sein, ich weiß es einfach nicht ... Sie müssten LogonUser mit einer anderen Domain ausprobieren, um dies herauszufinden.
Marc Gravell

3

OK ... ich kann resond ..

Haftungsausschluss: Ich hatte gerade einen Tag mit mehr als 18 Stunden (wieder). Ich bin alt und vergesslich. Ich kann nicht buchstabieren. Ich habe eine kurze Aufmerksamkeitsspanne, also reagiere ich besser schnell. :-)

Frage:

Ist es möglich, das Thread-Prinzip in einen Benutzer ohne Konto auf dem lokalen Computer zu ändern?

Antworten:

Ja, Sie können einen Thread-Principal ändern, auch wenn die von Ihnen verwendeten Anmeldeinformationen nicht lokal definiert sind oder sich außerhalb der "Gesamtstruktur" befinden.

Ich bin gerade auf dieses Problem gestoßen, als ich versucht habe, über einen Dienst eine Verbindung zu einem SQL Server mit NTLM-Authentifizierung herzustellen. Bei diesem Aufruf werden die mit dem Prozess verknüpften Anmeldeinformationen verwendet. Dies bedeutet, dass Sie entweder ein lokales Konto oder ein Domänenkonto zur Authentifizierung benötigen, bevor Sie sich als Benutzer ausgeben können. Bla, bla ...

Aber...

Wenn Sie LogonUser (..) mit dem Attribut ???? _ NEW_CREDENTIALS aufrufen, wird ein Sicherheitstoken zurückgegeben, ohne dass versucht wird, die Anmeldeinformationen zu authentifizieren. Kewl .. Sie müssen das Konto nicht innerhalb der "Gesamtstruktur" definieren. Sobald Sie das Token haben, müssen Sie möglicherweise DuplicateToken () mit der Option aufrufen, den Identitätswechsel zu aktivieren, was zu einem neuen Token führt. Rufen Sie nun SetThreadToken (NULL, Token) auf. (Es könnte & Token sein?) .. Ein Aufruf von ImpersonateLoggedonUser (Token); könnte erforderlich sein, aber ich denke nicht. Schlag es nach..

Tun Sie, was Sie tun müssen ..

Rufen Sie RevertToSelf () auf, wenn Sie ImpersonateLoggedonUser () aufgerufen haben, dann SetThreadToken (NULL, NULL); (Ich denke ... nachschlagen) und dann CloseHandle () auf den erstellten Handles.

Keine Versprechungen, aber das hat bei mir funktioniert ... Das ist mir auf den Kopf gefallen (wie meine Haare) und ich kann nicht buchstabieren !!!



1

Wird auch auf F # portiert , um mit FAKE verwendet zu werden

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

0

Sie sollten versuchen, ein solches hinzuzufügen:

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

In Ihre web.config.

Mehr Informationen.


Einige Unternehmenssicherheitsmaßnahmen verhindern die Verwendung von Identitätswechsel, da sie die Anwendung, die sie verwendet, nicht verfolgen können und sich in derselben oder einer vertrauenswürdigen Domäne befinden müssen. Ich denke, dass unpersönliche Unterstützung entdeckt wird. Ein Domain-Service-Konto mit Pinvoke scheint der richtige Weg zu sein.
Jim
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.