자격 증명을 사용하여 트러스트되지 않은 원격 도메인에서 공유 파일 (UNC)에 액세스


151

우리는 해결해야 할 흥미로운 상황에 처해 있었고 검색 결과가 조금씩 바뀌 었습니다. 따라서 SO 커뮤니티에 도움을 요청합니다.

문제는 이것입니다. 도메인에없고 원격 파일 공유 / UNC를 통해 신뢰할 수있는 외부 도메인 내에 있지 않은 공유 파일에 프로그래밍 방식으로 액세스해야합니다. 당연히 원격 시스템에 자격 증명을 제공해야합니다.

일반적으로 다음 두 가지 방법 중 하나로이 문제를 해결합니다.

  1. 파일 공유를 드라이브로 매핑하고 그때 자격 증명을 제공하십시오. 이것은 일반적으로 NET USE명령 또는 Win32 기능을 복제 하여 수행됩니다 NET USE.
  2. 원격 컴퓨터가 도메인에있는 것처럼 UNC 경로로 파일에 액세스하고 프로그램이 실행되는 계정이 원격 컴퓨터에서 로컬 사용자로 복제되었는지 (비밀번호 포함) 확인하십시오. 기본적으로 사용자가 공유 파일에 액세스하려고하면 Windows가 현재 사용자의 자격 증명을 자동으로 제공한다는 사실을 이용하십시오.
  3. 원격 파일 공유를 사용하지 마십시오. FTP (또는 다른 수단)를 사용하여 파일을 전송하고 로컬에서 작업 한 다음 다시 전송하십시오.

다양하고 햇볕이 잘 드는 이유 때문에 보안 / 네트워크 설계자는 처음 두 가지 접근 방식을 거부했습니다. 두 번째 접근법은 분명히 보안 허점입니다. 원격 컴퓨터가 손상된 경우 이제 로컬 컴퓨터가 위험에 노출됩니다. 새로 마운트 된 드라이브는 프로그램이 파일을 액세스하는 동안 로컬 컴퓨터의 다른 프로그램에서 사용할 수있는 공유 리소스이기 때문에 첫 번째 방법은 만족스럽지 않습니다. 이것을 일시적으로 만들 수는 있지만 여전히 의견에는 구멍이 있습니다.

세 번째 옵션을 사용할 수 있지만 원격 네트워크 관리자는 FTPS가 아니라 SFTP를 주장하며 FtpWebRequest는 FTPS 만 지원합니다. SFTP 방화벽에 더 친숙한 옵션이며 해당 접근 방식에 사용할 수있는 몇 가지 라이브러리가 있지만 가능한 경우 종속성을 줄이는 것을 선호합니다.

원격 파일 공유를 사용하는 관리 또는 win32 수단에 대해 MSDN을 검색했지만 유용한 것을 찾지 못했습니다.

그래서 묻습니다. 다른 방법이 있습니까? 내가 원하는 것을 수행하는 비밀 비밀 win32 기능을 놓쳤습니까? 아니면 옵션 3의 변형을 추구해야합니까?


가장 방법으로 문제를 해결했지만 도메인 외부의 두 컴퓨터 사이에 있습니다. 도메인에서 도메인 외부의 컴퓨터로 말하는 데 문제가 있는지 모르겠습니다. stackoverflow.com/questions/17221476/…
Wolf5

답변:


174

문제를 해결하는 방법은 WNetUseConnection 이라는 Win32 API를 사용하는 것 입니다.
드라이브를 맵핑하지 않고 인증으로 UNC 경로에 연결하려면이 기능을 사용하십시오 .

이렇게하면 동일한 도메인에 있지 않고 다른 사용자 이름과 암호를 사용하더라도 원격 시스템에 연결할 수 있습니다.

WNetUseConnection을 사용한 후에는 동일한 도메인에있는 것처럼 UNC 경로를 통해 파일에 액세스 할 수 있습니다. 가장 좋은 방법은 아마도 관리 내장 주식을 이용하는 것입니다.
예 : \\ computername \ c $ \ program files \ Folder \ file.txt

다음은 WNetUseConnection을 사용하는 샘플 C # 코드입니다.
NetResource의 경우 lpLocalName 및 lpProvider에 대해 널을 전달해야합니다. dwType은 RESOURCETYPE_DISK 여야합니다. lpRemoteName은 \\ ComputerName이어야합니다.

using System;
using System.Runtime.InteropServices ;
using System.Threading;

namespace ExtremeMirror
{
    public class PinvokeWindowsNetworking
    {
        #region Consts
        const int RESOURCE_CONNECTED = 0x00000001;
        const int RESOURCE_GLOBALNET = 0x00000002;
        const int RESOURCE_REMEMBERED = 0x00000003;

        const int RESOURCETYPE_ANY = 0x00000000;
        const int RESOURCETYPE_DISK = 0x00000001;
        const int RESOURCETYPE_PRINT = 0x00000002;

        const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
        const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
        const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
        const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
        const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
        const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

        const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
        const int RESOURCEUSAGE_CONTAINER = 0x00000002;


        const int CONNECT_INTERACTIVE = 0x00000008;
        const int CONNECT_PROMPT = 0x00000010;
        const int CONNECT_REDIRECT = 0x00000080;
        const int CONNECT_UPDATE_PROFILE = 0x00000001;
        const int CONNECT_COMMANDLINE = 0x00000800;
        const int CONNECT_CMD_SAVECRED = 0x00001000;

        const int CONNECT_LOCALDRIVE = 0x00000100;
        #endregion

        #region Errors
        const int NO_ERROR = 0;

        const int ERROR_ACCESS_DENIED = 5;
        const int ERROR_ALREADY_ASSIGNED = 85;
        const int ERROR_BAD_DEVICE = 1200;
        const int ERROR_BAD_NET_NAME = 67;
        const int ERROR_BAD_PROVIDER = 1204;
        const int ERROR_CANCELLED = 1223;
        const int ERROR_EXTENDED_ERROR = 1208;
        const int ERROR_INVALID_ADDRESS = 487;
        const int ERROR_INVALID_PARAMETER = 87;
        const int ERROR_INVALID_PASSWORD = 1216;
        const int ERROR_MORE_DATA = 234;
        const int ERROR_NO_MORE_ITEMS = 259;
        const int ERROR_NO_NET_OR_BAD_PATH = 1203;
        const int ERROR_NO_NETWORK = 1222;

        const int ERROR_BAD_PROFILE = 1206;
        const int ERROR_CANNOT_OPEN_PROFILE = 1205;
        const int ERROR_DEVICE_IN_USE = 2404;
        const int ERROR_NOT_CONNECTED = 2250;
        const int ERROR_OPEN_FILES  = 2401;

        private struct ErrorClass 
        {
            public int num;
            public string message;
            public ErrorClass(int num, string message) 
            {
                this.num = num;
                this.message = message;
            }
        }


        // Created with excel formula:
        // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
        private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
            new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
            new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
            new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
            new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
            new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
            new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
            new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
            new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
            new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
            new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
            new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
            new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
            new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
            new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
            new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
            new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
        };

        private static string getErrorForNumber(int errNum) 
        {
            foreach (ErrorClass er in ERROR_LIST) 
            {
                if (er.num == errNum) return er.message;
            }
            return "Error: Unknown, " + errNum;
        }
        #endregion

        [DllImport("Mpr.dll")] private static extern int WNetUseConnection(
            IntPtr hwndOwner,
            NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUserID,
            int dwFlags,
            string lpAccessName,
            string lpBufferSize,
            string lpResult
        );

        [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
            string lpName,
            int dwFlags,
            bool fForce
        );

        [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
        { 
            public int dwScope = 0;
            public int dwType = 0;
            public int dwDisplayType = 0;
            public int dwUsage = 0;
            public string lpLocalName = "";
            public string lpRemoteName = "";
            public string lpComment = "";
            public string lpProvider = "";
        }


        public static string connectToRemote(string remoteUNC, string username, string password) 
        {
            return connectToRemote(remoteUNC, username, password, false);
        }

        public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
        {
            NETRESOURCE nr = new NETRESOURCE();
            nr.dwType = RESOURCETYPE_DISK;
            nr.lpRemoteName = remoteUNC;
            //          nr.lpLocalName = "F:";

            int ret;
            if (promptUser) 
                ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
            else 
                ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);

            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }

        public static string disconnectRemote(string remoteUNC) 
        {
            int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }
    }
}

이와 같은 기능을 사용하여 현재 자격 증명을 사용하여, 즉 사용자 이름과 비밀번호를 제공하지 않고 네트워크 머신에 대한 연결을 명시 적으로 열고 닫을 수있는 방법이 있습니까? 파일 공유에 액세스 한 후 연결을 닫는 데 특히 관심이 있습니다.
flipdoubt

컴퓨터 자체에 사용자 이름이나 비밀번호가없는 경우를 제외하고는 연결하지 마십시오. 연결을 끊을 수 있습니다. 대신 명령 줄을 통해 수행 할 수도 있습니다.
Brian R. Bondy

1
안녕 브라이언. 링크 한 문서는 현재 자격 증명을 사용하기 위해 사용자 이름과 암호에 NULL을 전달할 수 있다고 말합니다. 이것이 작동하는지 확인하기 위해 몇 가지 테스트를 수행합니다.
flipdoubt

사용자 이름 / 암호에 null을 전달하면 연결할 수 있지만 연결이 끊 겼음을 어떻게 확인할 수 있습니까? 서버에서 볼 수있는 것이 있습니까? Server 2003에서는 세션을 볼 수 있지만 앱에서 이러한 API를 사용하지 않으면 현재 세션 목록이 빠르게 업데이트됩니다.
flipdoubt

? WNetUseConnection를 호출 하여 연결을 수동으로 닫아야 WNetCancelConnection2합니까? 아니면 유휴 시간 초과 (또는 다른 메커니즘)가 있고 귀찮게 할 필요가 있습니까?
파 128

123

빠른 해결책을 찾는 사람들에게는 NetworkShareAccesser최근에 쓴 ( 이 답변을 바탕으로 (감사합니다!))을 사용할 수 있습니다.

용법:

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
    File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

경고 : 것을 절대적으로 확인하시기 바랍니다 Dispose이의 NetworkShareAccesser(! 당신이 충돌되는 앱하더라도)라고, 그렇지 않으면 열려있는 연결 Windows에서 유지됩니다. cmd프롬프트 를 열고 를 입력 하면 열려있는 모든 연결을 볼 수 있습니다 net use.

코드:

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
    private string _remoteUncName;
    private string _remoteComputerName;

    public string RemoteComputerName
    {
        get
        {
            return this._remoteComputerName;
        }
        set
        {
            this._remoteComputerName = value;
            this._remoteUncName = @"\\" + this._remoteComputerName;
        }
    }

    public string UserName
    {
        get;
        set;
    }
    public string Password
    {
        get;
        set;
    }

    #region Consts

    private const int RESOURCE_CONNECTED = 0x00000001;
    private const int RESOURCE_GLOBALNET = 0x00000002;
    private const int RESOURCE_REMEMBERED = 0x00000003;

    private const int RESOURCETYPE_ANY = 0x00000000;
    private const int RESOURCETYPE_DISK = 0x00000001;
    private const int RESOURCETYPE_PRINT = 0x00000002;

    private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
    private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
    private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
    private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
    private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

    private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
    private const int RESOURCEUSAGE_CONTAINER = 0x00000002;


    private const int CONNECT_INTERACTIVE = 0x00000008;
    private const int CONNECT_PROMPT = 0x00000010;
    private const int CONNECT_REDIRECT = 0x00000080;
    private const int CONNECT_UPDATE_PROFILE = 0x00000001;
    private const int CONNECT_COMMANDLINE = 0x00000800;
    private const int CONNECT_CMD_SAVECRED = 0x00001000;

    private const int CONNECT_LOCALDRIVE = 0x00000100;

    #endregion

    #region Errors

    private const int NO_ERROR = 0;

    private const int ERROR_ACCESS_DENIED = 5;
    private const int ERROR_ALREADY_ASSIGNED = 85;
    private const int ERROR_BAD_DEVICE = 1200;
    private const int ERROR_BAD_NET_NAME = 67;
    private const int ERROR_BAD_PROVIDER = 1204;
    private const int ERROR_CANCELLED = 1223;
    private const int ERROR_EXTENDED_ERROR = 1208;
    private const int ERROR_INVALID_ADDRESS = 487;
    private const int ERROR_INVALID_PARAMETER = 87;
    private const int ERROR_INVALID_PASSWORD = 1216;
    private const int ERROR_MORE_DATA = 234;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
    private const int ERROR_NO_NETWORK = 1222;

    private const int ERROR_BAD_PROFILE = 1206;
    private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
    private const int ERROR_DEVICE_IN_USE = 2404;
    private const int ERROR_NOT_CONNECTED = 2250;
    private const int ERROR_OPEN_FILES = 2401;

    #endregion

    #region PInvoke Signatures

    [DllImport("Mpr.dll")]
    private static extern int WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        string lpPassword,
        string lpUserID,
        int dwFlags,
        string lpAccessName,
        string lpBufferSize,
        string lpResult
        );

    [DllImport("Mpr.dll")]
    private static extern int WNetCancelConnection2(
        string lpName,
        int dwFlags,
        bool fForce
        );

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
    }

    #endregion

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <returns></returns>
    public static NetworkShareAccesser Access(string remoteComputerName)
    {
        return new NetworkShareAccesser(remoteComputerName);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="domainOrComuterName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName,
                                        domainOrComuterName + @"\" + userName,
                                        password);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName, 
                                        userName,
                                        password);
    }

    private NetworkShareAccesser(string remoteComputerName)
    {
        RemoteComputerName = remoteComputerName;               

        this.ConnectToShare(this._remoteUncName, null, null, true);
    }

    private NetworkShareAccesser(string remoteComputerName, string userName, string password)
    {
        RemoteComputerName = remoteComputerName;
        UserName = userName;
        Password = password;

        this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
    }

    private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUnc
        };

        int result;
        if (promptUser)
        {
            result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        }
        else
        {
            result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        }

        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    private void DisconnectFromShare(string remoteUnc)
    {
        int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        this.DisconnectFromShare(this._remoteUncName);
    }
}

2
당신은 또한 필요 using System.Runtime.InteropServices;using System.ComponentModel;위해 DllImportWin32Exception
Kᴀτᴢ

이 솔루션은 긴 하루 검색을 중단했습니다. 감사!!! 필요에 따라 꽤 잘 작동합니다.
Venkat

1
원격 컴퓨터의 로컬 사용자 계정으로 솔루션을 사용하려고하는데 액세스 거부 오류가 계속 발생합니다. 귀하의 솔루션은 네트워크 계정에서만 작동합니까?
M3NTA7

1
계정이 원격 컴퓨터에 있지만 네트워크 계정이 아닙니다. 로컬 컴퓨터 계정입니다. 도메인을 컴퓨터 이름으로 설정하려고했습니다. 공유 폴더의 로컬 사용자 계정에 대한 전체 권한도 부여했지만 액세스가 거부되었습니다. 왜 이런 일이 일어날 수 있는지에 대한 아이디어가 있습니까? 고마워.
M3NTA7

2
참고 : 객체를 폐기해도 시스템에서 자격 증명이 삭제되지 않는 것 같습니다 (Windows 10). 연결이 "취소"된 후 원격 컴퓨터의 파일에 액세스 할 수 있습니다. 사용자 계정에 다시 로그인하거나 컴퓨터를 다시 시작하면이 내부 캐시가 지워진 것 같습니다.
Tim Cooper

16

AFAIK에서는 서버의 자격 증명을 설정하기 위해 UNC 경로를 드라이브 문자에 매핑 할 필요가 없습니다 . 나는 정기적으로 다음과 같은 배치 스크립트를 사용했다.

net use \\myserver /user:username password

:: do something with \\myserver\the\file\i\want.xml

net use /delete \\my.server.com

그러나 프로그램과 동일한 계정으로 실행되는 모든 프로그램은 여전히 ​​액세스 권한이있는 모든 항목에 username:password액세스 할 수 있습니다. 가능한 해결책은 자체 로컬 사용자 계정으로 프로그램을 분리하는 것입니다 (UNC 액세스는을 호출 한 계정에 로컬입니다 NET USE).

참고 : SMB 교차 도메인 사용은 IMO 기술을 잘 사용하지 않습니다. 보안이 그토록 중요하다면 SMB에 암호화가 없다는 사실은 그 자체로 약간 댐퍼입니다.


UNC 액세스가라는 계정에서만 사용할 수있는 것이 올바른 경우 NET USE에는 실행 가능한 방법 일 수 있습니다. 그러나 로컬 계정을 사용해야합니까? NET USE전화가 걸려온 컴퓨터에 국한 되지 않습니까? 당신은 저에게 좋은 연구 경로를주었습니다
Randolpho

AFAIK이며 잘못되었을 수 있습니다. UNC 액세스는 NET USE 호출이 이루어진 특정 보안 주체 (SAM 계정 등) 만 사용할 수 있습니다. RunAs를 사용하여 경로를 매핑 한 다음 다른 계정에서 경로를 액세스하여이를 확인할 수 있습니다.
Jacob

제 경우에는 사용자가 다른 도메인에 있으므로 net use \\ myserver / user : username @ domain 비밀번호를 사용해야했습니다.
StarCub

4

WNetUseConnection 대신 NetUseAdd를 권장 합니다 . WNetUseConnection은 WNetUseConnection2 및 WNetUseConnection3에 의해 대체 된 레거시 기능이지만 이러한 기능은 모두 Windows 탐색기에 표시되는 네트워크 장치를 만듭니다. NetUseAdd는 DOS 프롬프트에서 net use를 호출하여 원격 컴퓨터에서 인증하는 것과 같습니다.

NetUseAdd를 호출하면 이후 디렉토리 액세스 시도가 성공합니다.


1
@Adam Robinson : 사실이 아닙니다. 그러한 WNetUseConnection2 나 WNetUseConnection3은 없습니다. WNetAddConnection2 및 WnetAddConnection3이 WNetAddConnection을 대체한다고 생각합니다. 또한 귀하가 제공 한 정보는 사실이 아닙니다.
Brian R. Bondy

WNetUseConnection은 WNetAddConnection3과 유사하지만 맵핑 된 로컬 드라이브를 작성하는 선택적 기능도 있습니다. 당신이 사용할 필요가 없습니다.
Brian R. Bondy 2016 년

@ BrianR.Bondy 그들은 C #으로 구현되지 않고 실제로 존재합니다. 출처 : docs.microsoft.com/da-dk/windows/win32/api/lmuse/… 견적 : "WNetAddConnection2 및 WNetAddConnection3 함수를 사용하여 로컬 장치를 네트워크 리소스로 리디렉션 할 수도 있습니다."
Thomas Williams

4

나는 나 자신을 모르지만, 확실히 # 2가 잘못되기를 희망합니다 ... Windows가 자동으로 내 로그인 정보 (모든 암호를 거의 사용하지 않음)를 모든 컴퓨터에 제공하지 않을 것이라고 생각하고 싶습니다. 내 신뢰의 일부가 아닌 것은 물론입니다.

그럼에도 불구하고 가장 아키텍처를 탐색 했습니까? 코드는 다음과 유사합니다.

using (System.Security.Principal.WindowsImpersonationContext context = System.Security.Principal.WindowsIdentity.Impersonate(token))
{
    // Do network operations here

    context.Undo();
}

이 경우 token변수는 IntPtr입니다. 이 변수의 값을 얻으려면 관리되지 않는 LogonUser Windows API 함수를 호출해야합니다. pinvoke.net으로 의 빠른 여행 은 다음과 같은 서명을 제공합니다.

[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    out IntPtr phToken
);

사용자 이름, 도메인 및 비밀번호는 상당히 분명해 보입니다. dwLogonType 및 dwLogonProvider에 전달할 수있는 다양한 값을 살펴보고 필요에 가장 적합한 값을 결정하십시오.

여기에 확인할 수있는 두 번째 도메인이 없으므로이 코드는 테스트되지 않았지만이 과정을 제대로 진행해야합니다.


7
신뢰할 수없는 도메인의 로그인 ID를 사용하려고하면 가장이 작동하지 않습니다. 사용자 ID는 로컬로 로그온 할 수 있어야합니다.
Moose

예, 우리는이 경로를 시험해 보았습니다. @Moose가 말한 것처럼 : 도메인은 신뢰할 수 없으므로 가장이 작동하지 않습니다.
Randolpho

네, 일단 그 의견을 보았 기 때문에 NetUseAdd를 사용하여 답변을 게시 한 이유입니다 (NetUseAdd와 WNetAddConnection의 주요 차이점은 NetUseAdd는 Windows 탐색기에서 연결을 표시하지 않는다는 것입니다).
Adam Robinson

가장은 동일한 도메인에서 작동하지 않으며 테스트에서 Access Denied가 관리자 계정으로 공유 폴더의 파일을 읽으려고 계속 응답합니다 (두 컴퓨터의 관리자). 따라서 이것이 올바른 접근 방식이 아니라고 생각합니다.
lidermin

4

여기에 모든 부스러기가 제거 된 최소 POC 클래스

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

public class UncShareWithCredentials : IDisposable
{
    private string _uncShare;

    public UncShareWithCredentials(string uncShare, string userName, string password)
    {
        var nr = new Native.NETRESOURCE
        {
            dwType = Native.RESOURCETYPE_DISK,
            lpRemoteName = uncShare
        };

        int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
        if (result != Native.NO_ERROR)
        {
            throw new Win32Exception(result);
        }
        _uncShare = uncShare;
    }

    public void Dispose()
    {
        if (!string.IsNullOrEmpty(_uncShare))
        {
            Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
            _uncShare = null;
        }
    }

    private class Native
    {
        public const int RESOURCETYPE_DISK = 0x00000001;
        public const int CONNECT_UPDATE_PROFILE = 0x00000001;
        public const int NO_ERROR = 0;

        [DllImport("mpr.dll")]
        public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
            int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);

        [DllImport("mpr.dll")]
        public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);

        [StructLayout(LayoutKind.Sequential)]
        public class NETRESOURCE
        {
            public int dwScope;
            public int dwType;
            public int dwDisplayType;
            public int dwUsage;
            public string lpLocalName;
            public string lpRemoteName;
            public string lpComment;
            public string lpProvider;
        }
    }
}

\\server\share\folderw /를 직접 사용할 수 있으므로 사전 WNetUseConnection\\server파트로 분리 할 필요가 없습니다 .


2

대부분의 SFTP 서버는 SCP를 지원하므로 라이브러리를 찾기가 훨씬 쉽습니다. PuTTY에 포함 된 pscp와 같은 코드에서 기존 클라이언트를 호출 할 수도 있습니다 .

작업중인 파일 유형이 텍스트 또는 XML 파일과 같은 단순한 유형 인 경우 .NET Remoting 또는 웹 서비스와 같은 파일을 사용하여 파일을 조작하기 위해 자체 클라이언트 / 서버 구현을 작성할 수 있습니다.


1

JScape 도구 를 사용하여 옵션 3을 매우 간단하게 구현하는 것을 보았습니다 . 시도해 볼 수 있습니다. 무료는 아니지만 업무를 수행합니다.


1

브라이언 참조에 따라 내 vb.net 코드 를 첨부하십시오.

Imports System.ComponentModel

Imports System.Runtime.InteropServices

Public Class PinvokeWindowsNetworking

Const NO_ERROR As Integer = 0



Private Structure ErrorClass

    Public num As Integer

    Public message As String



    Public Sub New(ByVal num As Integer, ByVal message As String)

        Me.num = num

        Me.message = message

    End Sub

End Structure



Private Shared ERROR_LIST As ErrorClass() = New ErrorClass() {

    New ErrorClass(5, "Error: Access Denied"),

    New ErrorClass(85, "Error: Already Assigned"),

    New ErrorClass(1200, "Error: Bad Device"),

    New ErrorClass(67, "Error: Bad Net Name"),

    New ErrorClass(1204, "Error: Bad Provider"),

    New ErrorClass(1223, "Error: Cancelled"),

    New ErrorClass(1208, "Error: Extended Error"),

    New ErrorClass(487, "Error: Invalid Address"),

    New ErrorClass(87, "Error: Invalid Parameter"),

    New ErrorClass(1216, "Error: Invalid Password"),

    New ErrorClass(234, "Error: More Data"),

    New ErrorClass(259, "Error: No More Items"),

    New ErrorClass(1203, "Error: No Net Or Bad Path"),

    New ErrorClass(1222, "Error: No Network"),

    New ErrorClass(1206, "Error: Bad Profile"),

    New ErrorClass(1205, "Error: Cannot Open Profile"),

    New ErrorClass(2404, "Error: Device In Use"),

    New ErrorClass(2250, "Error: Not Connected"),

    New ErrorClass(2401, "Error: Open Files")}



Private Shared Function getErrorForNumber(ByVal errNum As Integer) As String

    For Each er As ErrorClass In ERROR_LIST

        If er.num = errNum Then Return er.message

    Next



    Try

        Throw New Win32Exception(errNum)

    Catch ex As Exception

        Return "Error: Unknown, " & errNum & " " & ex.Message

    End Try



    Return "Error: Unknown, " & errNum

End Function



<DllImport("Mpr.dll")>

Private Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer

End Function



<DllImport("Mpr.dll")>

Private Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer

End Function



<StructLayout(LayoutKind.Sequential)>

Private Class NETRESOURCE

    Public dwScope As Integer = 0

    Public dwType As Integer = 0

    Public dwDisplayType As Integer = 0

    Public dwUsage As Integer = 0

    Public lpLocalName As String = ""

    Public lpRemoteName As String = ""

    Public lpComment As String = ""

    Public lpProvider As String = ""

End Class



Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String) As String

    Return connectToRemote(remoteUNC, username, password, False)

End Function



Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String, ByVal promptUser As Boolean) As String

    Dim nr As NETRESOURCE = New NETRESOURCE()

    nr.dwType = ResourceTypes.Disk

    nr.lpRemoteName = remoteUNC

    Dim ret As Integer



    If promptUser Then

        ret = WNetUseConnection(IntPtr.Zero, nr, "", "", Connects.Interactive Or Connects.Prompt, Nothing, Nothing, Nothing)

    Else

        ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, Nothing, Nothing, Nothing)

    End If



    If ret = NO_ERROR Then Return Nothing

    Return getErrorForNumber(ret)

End Function



Public Shared Function disconnectRemote(ByVal remoteUNC As String) As String

    Dim ret As Integer = WNetCancelConnection2(remoteUNC, Connects.UpdateProfile, False)

    If ret = NO_ERROR Then Return Nothing

    Return getErrorForNumber(ret)

End Function


Enum Resources As Integer

    Connected = &H1

    GlobalNet = &H2

    Remembered = &H3

End Enum


Enum ResourceTypes As Integer

    Any = &H0

    Disk = &H1

    Print = &H2

End Enum


Enum ResourceDisplayTypes As Integer

    Generic = &H0

    Domain = &H1

    Server = &H2

    Share = &H3

    File = &H4

    Group = &H5

End Enum


Enum ResourceUsages As Integer

    Connectable = &H1

    Container = &H2

End Enum


Enum Connects As Integer

    Interactive = &H8

    Prompt = &H10

    Redirect = &H80

    UpdateProfile = &H1

    CommandLine = &H800

    CmdSaveCred = &H1000

    LocalDrive = &H100

End Enum


End Class

사용 방법

Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If

-1

나는 답을 찾기 위해 MS를 보았다. 첫 번째 솔루션은 응용 프로그램 프로세스를 실행하는 사용자 계정이 공유 폴더 또는 드라이브 (동일한 도메인)에 액세스 할 수 있다고 가정합니다. DNS가 해결되었는지 확인하거나 IP 주소를 사용해보십시오. 간단히 다음을 수행하십시오.

 DirectoryInfo di = new DirectoryInfo(PATH);
 var files = di.EnumerateFiles("*.*", SearchOption.AllDirectories);

자격 증명이있는 다른 도메인 .NET 2.0에서이 모델을 따르십시오.

WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));

        req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
        req.PreAuthenticate = true;

        WebResponse d = req.GetResponse();
        FileStream fs = File.Create("test.txt");

        // here you can check that the cast was successful if you want. 
        fs = d.GetResponseStream() as FileStream;
        fs.Close();

재미있어 보인다
DeerSpotter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.