首页 文章

通过指定凭据访问服务器上的文件

提问于
浏览
2

我们公司有一个共享点文档服务器,UNC看起来像这样:\ theserver.ourdomain.com \ rootdirectory

目前,此驱动器映射到本地计算机上的Z:\ . 要访问Z:\您必须指定(每次登录时)凭据(在我们的例子中是我们登录的用户名和密码)来访问 rootdirectory 中的文件夹和文件 .

我处于需要将文件复制到共享点服务器的情况 . 我希望能够在不使用映射的网络驱动器的情况下将文件复制到服务器上(不必在路径中指定Z:\) . 我如何提供凭据,以便我可以执行基本IO功能,如GetDirectories(),GetFiles(),IO.File.Copy()等...?

我已经研究了以下事项,但是没有成功使它们工作:

  • LogonUser API调用通过指定纯文本用户名和密码,然后从该调用中获取令牌并使用WindowsIdentity类的新实例模拟该用户 . 能够获得令牌,但模仿似乎不起作用 . 保持访问被拒绝错误 .

  • CredUIPromptForCredentials / CredUIPromptForWindowsCredentials API调用,但我意识到这些仅适用于花哨的Windows用户界面,您可以在其中输入凭据并且实际上不执行任何操作 .

<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function LogonUser(lpszUsername As String, lpszDomain As String, _
                                          lpszPassword As String, dwLogonType As Integer, _
                                          dwLogonProvider As Integer, ByRef phToken As IntPtr) As Boolean
End Function

<DllImport("kernel32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function CloseHandle(handle As IntPtr) As Boolean
End Function

'// logon types
Public Const LOGON32_LOGON_NETWORK As Integer = 3
Public Const LOGON32_LOGON_NEW_CREDENTIALS As Integer = 9

'// logon providers
Public Const LOGON32_PROVIDER_WINNT50 As Integer = 3
Public Const LOGON32_PROVIDER_WINNT40 As Integer = 2
Public Const LOGON32_PROVIDER_WINNT35 As Integer = 1
Public Const LOGON32_PROVIDER_DEFAULT As Integer = 0

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim token = IntPtr.Zero
    Dim success = LogonUser("username", "domain", "password", _
                            LOGON32_LOGON_NEW_CREDENTIALS, _
                            LOGON32_PROVIDER_DEFAULT, token)

    If Not success Then
        Me.RaiseLastWin32Error()
    End If

    Using identity = New WindowsIdentity(token)
        Using impersonated = identity.Impersonate()
            Try
                Dim info = New DirectoryInfo("\\theserver.ourdomain.com\rootdirectory\")
                Dim files = info.GetDirectories()
            Catch ex As Exception
            Finally
                impersonated.Undo()
            End Try
            If Not CloseHandle(token) Then
                Me.RaiseLastWin32Error()
            End If
        End Using
    End Using

End Sub

Private Sub RaiseLastWin32Error()
    Dim hr = Marshal.GetLastWin32Error()
    Dim ex = Marshal.GetExceptionForHR(hr)

    If ex IsNot Nothing Then
        Throw ex
    End If
    Throw New SystemException(String.Format("Call resulted in error code {0}", hr))
End Sub

2 回答

  • 0

    这不是您问题的直接答案,因为它是一种截然不同的方法 . 如果它不适合您的情况抱歉打扰,但您是否考虑过使用SharePoint Web服务加载文件并检索信息?

    我建议这种方法有几个原因:

    • 您遇到的问题可能正在发生,因为SharePoint实现的WebDav可能与System.IO不是100%兼容 . 我肯定知道兼容性,但似乎有道理 .

    • 您可以轻松地将您拥有的UNC位置按摩到Web服务所需的URL中 .

    • 您可以直接在代理上设置凭据,可能会更容易 . (虽然我们从另一个Web服务器进行这些调用,因此示例中的应用程序池凭据对我们来说已经足够了)

    以下是一些清理和简化的代码,以防万一:

    // location takes the form http://server.name.com/site/library/folder/document.ext
    
    public string UploadDocument(string location, byte[] fileContents)
    {
        var result = String.empty;
        var destination = new string[1];
        destination[0] = location;
        var fileName = Path.GetFileName(location);
        var fieldInfo = new FieldInformation[0];
        CopyResult[] copyResults;
    
        _copyService.Url = "http://server.name.com/_vti_bin/Copy.asmx";
        _copyService.Credentials = CredentialCache.DefaultCredentials;
        _copyService.CopyIntoItems(fileName, destination, fieldInfo, fileContents, out copyResults);
    
        var errorCode = copyResults[0].ErrorCode;
        if (errorCode != CopyErrorCode.Success)
        {
            if (errorCode == CopyErrorCode.DestinationCheckedOut)
                result = "File is currently checked out. Please try again later.";
            else
                result = "Error uploading content.";
        }
    
        return result;
    }
    

    _copyService是我们注入的依赖项,其中运行时实现是由Copy.asmx SharePoint Web服务中的Visual Studio工具生成的代理 .

    您还可以使用Lists.asmx Web服务获取文件夹内容和文档元数据 . 这种方法的最大缺点是查询信息需要一些CAML知识,处理结果并不容易 . 但是这些服务在MSDN上得到了合理的记录,并且这些操作都在我们的应用程序中运行 .

  • 0

    好吧,我能够借助WNetAddConnection2 API解决这个问题 . 此API也用于映射网络驱动器,但是您可以在不指定驱动器号的情况下调用此方法,以便只添加连接 .

    比方说你有驱动器X:映射到\ server \ share也让我们说它需要用户名和密码才能访问服务器上的文件 . 当您重新启动Windows 7时,您可能会丢失该连接(您将收到一条通知,告知Windows无法重新连接某些网络驱动器) . 如果您的应用程序需要访问该服务器的文件,并且您尝试在不提供凭据的情况下访问它,则会获得访问被拒绝的异常 . 如果您成功调用WNetAddConnection2,它不仅会修复未映射的网络驱动器,还可以通过System.IO命名空间访问文件/目录 .

    我们使用Sharepoint,这对我有用 . 感谢其他人的回复 .

相关问题