【问题标题】:Accessing Files On Server By Specifying Credentials通过指定凭据访问服务器上的文件
【发布时间】:2012-06-12 20:36:47
【问题描述】:

我们公司有一个共享点文档服务器,其中 UNC 如下所示:\\theserver.ourdomain.com\rootdirectory

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

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

我研究了以下事情,但未能成功:

  1. LogonUser API 调用,通过指定纯文本用户名和密码,然后从该调用中获取令牌并使用 WindowsIdentity 类的新实例模拟该用户。能够获得令牌,但模拟似乎不起作用。不断收到拒绝访问错误。
  2. CredUIPromptForCredentials/CredUIPromptForWindowsCredentials API 调用,但我意识到这些只是用于一个花哨的 Windows UI,您可以在其中输入您的凭据,实际上什么都不做。

    <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
    

【问题讨论】:

  • 另一个 MSDN 资源:msdn.microsoft.com/en-us/library/chf6fbt4.aspx。意识到您已经尝试过(您的帖子中的#1),您可能需要实际显示一些代码来诊断您遇到异常的原因。
  • 在上面的帖子中添加了代码
  • other post 中的代码和 MSDN 示例中的代码都比您尝试过的稍有不同且更复杂。给他们一个机会,然后报告。
  • 试过了,但它们不起作用。仍然收到拒绝访问错误。认为我们中的一个人不理解某事。我不必冒充自己来访问服务器,我已经以要访问文件的用户身份登录。一定有什么我错过了。

标签: vb.net authentication sharepoint unc


【解决方案1】:

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

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

  1. 您遇到的问题可能是因为 SharePoint 实施的 WebDav 可能与 System.IO 不是 100% 兼容。我不是这里的内脏方面的专家,我不确定兼容性,但这似乎是合理的。
  2. 您拥有的 UNC 位置可以轻松地转换为 Web 服务所需的 URL。
  3. 您可以直接在代理上设置凭据,这样可能会更轻松。 (尽管我们从另一个 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 是我们注入的依赖项,其中运行时实现是由 Visual Studio 工具从 Copy.asmx SharePoint Web 服务生成的代理。

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

【讨论】:

  • 感谢您的回复。如果我(通过资源管理器)向映射的网络驱动器提供我的密码和用户名,我可以通过代码访问文件和目录。如果我按照您建议的路线进行操作,我也会担心性能问题。
  • 有趣。届时我会很想自己找出解决方案,因为我从性能角度了解您的想法。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-15
    • 2020-04-04
    • 2012-03-16
    • 2016-11-01
    • 2013-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多