Cách cung cấp tên người dùng và mật khẩu khi kết nối với mạng chia sẻ


191

Khi kết nối với chia sẻ mạng mà người dùng hiện tại (trong trường hợp của tôi, người dùng dịch vụ kích hoạt mạng) không có quyền, tên và mật khẩu phải được cung cấp.

Tôi biết cách thực hiện điều này với các chức năng Win32 ( WNet*gia đình từ mpr.dll), nhưng muốn thực hiện với chức năng .Net (2.0).

Có những lựa chọn nào?

Có thể một số thông tin thêm giúp:

  • Ca sử dụng là dịch vụ windows, không phải ứng dụng Asp.Net.
  • Dịch vụ này đang chạy trong một tài khoản không có quyền đối với chia sẻ.
  • Tài khoản người dùng cần thiết cho việc chia sẻ không được biết đến ở phía khách hàng.
  • Máy khách và máy chủ không phải là thành viên của cùng một tên miền.

7
Mặc dù tôi không cung cấp cho bạn câu trả lời hữu ích, tôi có thể cung cấp câu trả lời chống lại. Việc mạo danh và tạo ra một quy trình như Marc đặt ra sẽ không hoạt động khi máy chủ và máy khách không ở cùng một miền, trừ khi có sự tin tưởng giữa hai miền. Nếu có một sự tin tưởng thì tôi nghĩ nó sẽ hoạt động. Tôi sẽ chỉ trả lời như một bình luận cho Marc nhưng tôi không có đủ đại diện để bình luận. : - /
Moose

Câu trả lời:


152

Bạn có thể thay đổi danh tính chủ đề hoặc P / Gọi WNetAddConnection2. Tôi thích cái sau, vì đôi khi tôi cần duy trì nhiều thông tin đăng nhập cho các địa điểm khác nhau. Tôi gói nó vào IDis Dùng một lần và gọi WNet HủyConnection2 để xóa tín dụng sau đó (tránh lỗi nhiều tên người dùng):

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

4
Dịch vụ không phải là thành viên của miền đích - việc mạo danh không thể hoạt động do bạn không thể tạo mã thông báo bảo mật cục bộ và mạo danh với nó. PInvoke là cách duy nhất.
stephbu

@MarkBrackett Tôi biết đây là một câu trả lời cũ, nhưng có lẽ bạn vẫn biết ... quyền truy cập chỉ được cấp cho chương trình hay cho người dùng đã đăng nhập thông qua explorer?
Cơn gió

@Breeze - Tôi chưa thử nghiệm nó, nhưng tôi hy vọng nó sẽ xác thực cho phiên đăng nhập; vì vậy nếu chương trình của bạn đang chạy như người dùng đã đăng nhập, họ cũng sẽ có quyền truy cập (ít nhất là trong thời gian hoạt động).
Mark Brackett

8
Các định nghĩa của readCredentials và writeCredentials có thể được bao gồm trong câu trả lời.
Anders Lindén

2
Nếu bạn đang gặp Lỗi 53 , hãy đảm bảo rằng đường dẫn không kết thúc bằng "\"
Mustafa S.

326

Tôi thích câu trả lời của Mark Brackett đến nỗi tôi đã tự mình thực hiện nhanh chóng. Đây là nếu bất cứ ai khác cần nó vội vàng:

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
Thật sự là như vậy throw new Win32Exception(result);, vì WNetAddConnection2 trả về mã lỗi win32 ( ERROR_XXX)
torvin

2
Đây là một đoạn mã nhỏ tuyệt vời. Cần phải đăng nhập vào một hệ thống UNIX để có được một danh sách thư mục để in ra một ứng dụng web MVC5 và điều này đã làm được điều đó. +1 !!!
Tây

3
Các câu lệnh sử dụng sau đây là bắt buộc để mã bên trên biên dịch: bằng System.Net; sử dụng System.R.78.InteropService; sử dụng System.ComponentModel;
Matt Nelson

4
xin lỗi để làm mới chủ đề cũ đó, nhưng có vẻ như nó không kết nối chặt chẽ sau khi kết thúc khối. Tôi có một chương trình để tải lên một vài hình ảnh, lần đầu tiên diễn ra tốt đẹp, lần thứ hai thất bại. Kết nối được giải phóng khi chương trình được đóng lại. Có lời khuyên nào không?
arti

3
Chúng tôi đã có cùng một vấn đề như bạn, @arti. Chỉ cần đặt tên người dùng và mật khẩu trên NetworkCredentialđối tượng, ứng dụng đã có thể kết nối một lần với ổ đĩa mạng. Sau đó, chúng tôi đã nhận được ERROR_LOGON_FAILURE cho mỗi lần thử cho đến khi ứng dụng được khởi động lại. Sau đó, chúng tôi đã cố gắng cung cấp tên miền trên NetworkCredentialđối tượng, và đột nhiên nó hoạt động! Tôi không biết tại sao điều này đã khắc phục vấn đề, đặc biệt là thực tế là nó hoạt động để kết nối một lần mà không có tên miền.
vào

50

Hôm nay 7 năm sau tôi đang đối mặt với cùng một vấn đề và tôi muốn chia sẻ phiên bản giải pháp của mình.

Nó đã được sao chép và dán sẵn sàng :-) Đây là:

Bước 1

Trong mã của bạn (bất cứ khi nào bạn cần làm gì đó với quyền)

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

Bước 2

Tệp trợ giúp thực hiện phép thuật

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 Theo tài liệu trên LogonUser , nó chỉ hoạt động cho người dùng trên máy tính cục bộ: "Hàm LogonUser cố gắng đăng nhập người dùng vào máy tính cục bộ. Máy tính cục bộ là máy tính mà LogonUser được gọi. Bạn không thể sử dụng LogonUser. để đăng nhập vào máy tính từ xa. "Bạn sẽ nhận được lỗi" Win32Exception: Tên người dùng hoặc mật khẩu không chính xác. " Vì vậy, tôi cho rằng các máy ít nhất phải ở trên cùng một miền.
Charles Chen

1
@CharlesChen Chỉ cần chứng minh rằng điều này hoạt động tốt trên các tên miền, FYI. Máy chủ tôi đang chạy này nằm trong DMZ và chắc chắn đang kết nối với máy chủ tệp trên một miền khác, thông qua tường lửa. Kẻ giết người Pavel, bạn là người đàn ông, và đây có lẽ nên là câu trả lời được chấp nhận ngày hôm nay.
Brian MacKay

Đây là một GIẢI PHÁP TUYỆT VỜI! Cảm ơn bạn, Pavel Kovalev.
STLDev

cái này có hoạt động trên ldap không? nó nói rằng tôi không có sẵn máy chủ đăng nhập. tôi đang sử dụng ldap auth
Julius Limson

28

Tôi đã tìm kiếm rất nhiều phương pháp và tôi đã làm theo cách của riêng mình. Bạn phải mở một kết nối giữa hai máy thông qua lệnh nhắc lệnh NET USE và sau khi hoàn thành công việc, hãy xóa kết nối với dấu nhắc lệnh NET USE "myconnection" / xóa.

Bạn phải sử dụng quy trình Dấu nhắc Lệnh từ mã phía sau như thế này:

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

Cách sử dụng rất đơn giản:

SaveACopyfileToServer(filePath, savePath);

Đây là chức năng:

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

Và chức năng ExecuteCommand cũng là:

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

Chức năng này làm việc rất nhanh và ổn định đối với tôi.


1
Trong trường hợp ánh xạ chia sẻ thất bại, mã trả về sẽ là gì?
chắc chắn

14

Giải pháp Luke Quinane có vẻ tốt, nhưng chỉ hoạt động một phần trong ứng dụng ASP.NET MVC của tôi. Có hai cổ phiếu trên cùng một máy chủ với các thông tin khác nhau, tôi chỉ có thể sử dụng mạo danh cho lần đầu tiên.

Vấn đề với WNetAddConnection2 cũng là nó hoạt động khác nhau trên các phiên bản windows khác nhau. Đó là lý do tại sao tôi tìm kiếm các lựa chọn thay thế và tìm thấy hàm LogonUser . Đây là mã của tôi cũng hoạt động trong 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;
    }
}

Sử dụng:

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

//do your stuff here

impersonationContext.Leave();

2
Cách tiếp cận này hoạt động tốt với tôi, nhưng nhận thấy trong thử nghiệm của tôi rằng khi sử dụng mật khẩu xấu với tài khoản người dùng tên miền, người dùng đó ngay lập tức bị rơi vào tình trạng bị khóa. chính sách tên miền của chúng tôi yêu cầu 3 lần đăng nhập thất bại trước khi điều đó xảy ra, nhưng thông qua cách tiếp cận này, một nỗ lực xấu và bạn đã bị khóa. Vì vậy, hãy thận trọng khi sử dụng ...
kellyb

5

Đối với VB.lovers, VB.NET tương đương với mã của Luke Quinane (cảm ơn 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

Một tùy chọn có thể hoạt động là sử dụng WindowsIdentity.Impersonate(và thay đổi chủ đề chính) để trở thành người dùng mong muốn, như vậy . Tuy nhiên, quay lại p / invoke, tôi sợ ...

Một lựa chọn táo bạo khác (và không kém lý tưởng) có thể là sinh ra một quy trình để thực hiện công việc ... ProcessStartInfochấp nhận một .UserName, .Password.Domain .

Cuối cùng - có lẽ chạy dịch vụ trong một tài khoản chuyên dụng có quyền truy cập? (loại bỏ vì bạn đã làm rõ rằng đây không phải là một tùy chọn).


Tôi không nghĩ rằng quá trình này là một ý tưởng tồi. google đưa ra một số trang trắng về lợi ích của đa xử lý trong chrome.
Dustin Getz

Có thể thay đổi chủ đề chính thành người dùng không có tài khoản trên máy cục bộ không?
gyrolf

Thành thật mà nói, tôi chỉ đơn giản là không biết ... Bạn phải thử LogonUser với một tên miền khác để tìm hiểu.
Marc Gravell

3

OK ... tôi có thể bán lại ..

Tuyên bố miễn trừ trách nhiệm: Tôi vừa có hơn 18 giờ một ngày (một lần nữa) .. Tôi già và hay quên .. Tôi không thể đánh vần .. Tôi có một khoảng chú ý ngắn để tôi phản hồi nhanh hơn .. :-)

Câu hỏi:

Có thể thay đổi chủ đề chính thành người dùng không có tài khoản trên máy cục bộ không?

Câu trả lời:

Có, bạn có thể thay đổi hiệu trưởng luồng ngay cả khi thông tin đăng nhập bạn đang sử dụng không được xác định cục bộ hoặc nằm ngoài "khu rừng".

Tôi vừa gặp vấn đề này khi cố gắng kết nối với máy chủ SQL bằng xác thực NTLM từ một dịch vụ. Cuộc gọi này sử dụng thông tin đăng nhập được liên kết với quy trình có nghĩa là bạn cần một tài khoản cục bộ hoặc tài khoản miền để xác thực trước khi bạn có thể mạo danh. Blah, blah ...

Nhưng...

Gọi LogonUser (..) với thuộc tính ???? _ NEW_CREDENTIALS sẽ trả về mã thông báo bảo mật mà không cố gắng xác thực thông tin đăng nhập. Kewl .. Đừng phải xác định tài khoản trong "khu rừng". Khi bạn có mã thông báo, bạn có thể phải gọi phó bản () với tùy chọn kích hoạt mạo danh dẫn đến mã thông báo mới. Bây giờ gọi SetThreadToken (NULL, mã thông báo); (Nó có thể là & mã thông báo?) .. Một cuộc gọi đến ImpersonateLoggedonUser (mã thông báo); có thể được yêu cầu, nhưng tôi không nghĩ vậy. Tìm kiếm..

Hãy làm những gì bạn cần làm..

Gọi RevertToSelf () nếu bạn gọi ImpersonateLoggedonUser () sau đó SetThreadToken (NULL, NULL); (Tôi nghĩ rằng ... hãy tìm nó), và sau đó là CloseHandle () trên các tay cầm đã tạo ..

Không có lời hứa nhưng điều này có hiệu quả với tôi ... Đây là trên đỉnh đầu của tôi (như tóc của tôi) và tôi không thể đánh vần !!!


1

Nếu bạn không thể tạo mã thông báo bảo mật hợp lệ cục bộ, có vẻ như bạn đã loại trừ tất cả các thanh tùy chọn Win32 API và WNetAddConnection *.

Hàng tấn thông tin trên MSDN về WNet - Thông tin PInvoke và mã mẫu kết nối với đường dẫn UNC tại đây:

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

Tham khảo MSDN tại đây:

http://msdn.microsoft.com/en-us/l Library / aa385391 (VS85) .aspx


1

Cũng được chuyển đến F # để sử dụng với 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

0

Bạn nên xem xét thêm như thế này:

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

Vào web.config của bạn.

Thêm thông tin.


Một số Bảo mật Công ty ngăn chặn việc sử dụng mạo danh vì họ không thể theo dõi ứng dụng bằng ứng dụng đó và phải ở trong cùng một miền hoặc đáng tin cậy. Tôi nghĩ rằng hỗ trợ mạo danh được phát hiện. Một tài khoản dịch vụ tên miền với pinvoke dường như là cách để đi.
Jim
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.