VB.NET - 如何以编程方式将身份验证传递给服务器 - 如何访问需要身份验证的服务器上的文件
VB.NET - How to programmatically pass authentication to a server - How to access files on server that requires authentication
我的目标:
我正在尝试访问我们网络上服务器的 C 驱动器,目的是搜索某个文件扩展名并返回这些文件的路径。我发现代码 here 可以完美运行,但前提是我正在通过我的本地计算机或我已经通过身份验证的服务器的 IP 进行搜索;当我尝试 运行 我尚未验证的服务器 IP 的代码不起作用时。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Cursor = Cursors.WaitCursor
DirSearch("\10.0.15.87\c$")
Cursor = Cursors.Default
End Sub
Public Sub DirSearch(ByVal sDir As String)
Try
For Each dir As String In Directory.GetDirectories(sDir)
For Each file In Directory.GetFiles(dir, "myfile.extension")
MsgBox(file)
Next
DirSearch(dir)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
End Sub
当我运行代码:
我相信代码执行完成;光标变成 WaitCursor 大约半秒钟,然后恢复正常,此时不会抛出任何错误,一切都将继续正常进行。如果我将“10.0.15.87”更改为“10.0.15.81”(我已经手动对其进行身份验证),那么我会弹出一些带有文件路径的 MsgBox。 (我确定问题不在于搜索文件,我知道它适用于其他 ip,并且我正在搜索的文件位于 10.0.15.87 的 c 驱动器中 - 我相信我只需要通过身份验证即可。 )
这是我尝试手动浏览到 ip 87 时发生的情况:
关于 10.0.15.87 的信息:
- 与我 运行 从中获取代码的计算机不同,它没有连接到域。 (两者都不是 10.0.15.81)
- 它有一个静态ip
- 如果我手动尝试访问它,我可以按照上图输入凭据,我只需要通过代码传递凭据。
如何在尝试访问服务器之前通过身份验证?
更新:我相信我已经找到了最简单和最一致的方法,方法 2 和 3 可能会或可能不会始终如一地工作。
方法 1 - 调用 Net Use(一致且跨域工作)
Net Use Command 允许您通过命令提示符将凭据存储到共享,并且您可以非常轻松地从 .NET 调用它。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim proc As New Process
proc.StartInfo.FileName = "net"
proc.StartInfo.UseShellExecute = True
proc.EnableRaisingEvents = False
Dim server As String = "Server"
Dim user As String = "User"
Dim pass As String = "Pass"
If EnsureConnection(server) Then
proc.StartInfo.Arguments = "use \" & server & "\IPC$ /u:" & server & "\" & user & " " & pass
proc.Start()
End If
End Sub
Function EnsureConnection(server As String)
'Give more or less ping attempts depending on how reliable your connection is.
'I have found that one ping can give false negative easily even on reliable connections
If My.Computer.Network.Ping(server) Then
Return True
ElseIf My.Computer.Network.Ping(server) Then
Return True
Else
Return False
End If
End Function
此方法会将凭据存储在机器上,直到机器关闭或重新启动,并且用户也将在程序之外进行身份验证。如果这不是您想要的,您可以随时使用命令的“/delete”开关在程序完成后立即将其删除。
方法 2 - 使用 MPR.DLL
中的函数
我终于找到了this answer另一个问题。这是对我有用的方法。这就是它集成到我的项目中的样子:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Form1
Dim user As String
Dim pass As String
Dim path As String
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Cursor = Cursors.WaitCursor
user = "UsernameString"
pass = "PasswordString"
path = "\10.0.15.87\c$"
MPRAuth()
DirSearch(path)
Cursor = Cursors.Default
End Sub
Private Sub MPRAuth()
Dim nr As New NETRESOURCE
nr.dwType = RESOURCETYPE_DISK
nr.lpRemoteName = path
If WNetAddConnection2(nr, pass, user, 0) <> NO_ERROR Then
Throw New Exception("WNetAddConnection2 failed.")
End If
If WNetCancelConnection2(path, 0, True) <> NO_ERROR Then
Throw New Exception("WNetCancelConnection2 failed.")
End If
End Sub
Public Sub DirSearch(ByVal sDir As String)
Try
For Each dir As String In Directory.GetDirectories(sDir)
Try
For Each file In Directory.GetFiles(dir, "*.exe")
MsgBox(file)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
DirSearch(dir)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
End Sub
<StructLayout(LayoutKind.Sequential)>
Private Structure NETRESOURCE
Public dwScope As UInteger
Public dwType As UInteger
Public dwDisplayType As UInteger
Public dwUsage As UInteger
<MarshalAs(UnmanagedType.LPTStr)>
Public lpLocalName As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpRemoteName As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpComment As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpProvider As String
End Structure
Private Const NO_ERROR As UInteger = 0
Private Const RESOURCETYPE_DISK As UInteger = 1
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetAddConnection2(ByRef lpNetResource As NETRESOURCE, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpPassword As String, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpUserName As String, ByVal dwFlags As UInteger) As UInteger
End Function
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetCancelConnection2(<[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpName As String, ByVal dwFlags As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal fForce As Boolean) As UInteger
End Function
End Class
方法 3 - NetworkCredential 和 CredentialCache
此方法将 ip 转换为 URI 并缓存凭据。 我无法在与我不同域的服务器上使用此方法。
Imports System.Net
Dim builder As New UriBuilder("10.0.15.87")
Dim uri As Uri = builder.Uri
Dim netCred = New NetworkCredential("UsernameString", "PasswordString", "DomainString")
Dim netCache = New CredentialCache()
netCache.Add(uri, "Basic", netCred)
DirSearch("\10.0.15.87\c$")
方法 2 比方法 2 简单得多,只要我尝试连接的服务器与我在同一个域中,它对我来说就可以正常工作。当我尝试连接到不在域中的服务器时,我收到错误 'User name or password is incorrect',这不是真的,所以我不确定问题出在哪里 - 也许其他人知道。到目前为止,方法 2 对我来说对任何服务器都完美无缺,无论域是什么,或者是否在 运行 代码之前对其进行了手动身份验证。希望这对其他人有帮助!
我的目标:
我正在尝试访问我们网络上服务器的 C 驱动器,目的是搜索某个文件扩展名并返回这些文件的路径。我发现代码 here 可以完美运行,但前提是我正在通过我的本地计算机或我已经通过身份验证的服务器的 IP 进行搜索;当我尝试 运行 我尚未验证的服务器 IP 的代码不起作用时。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Cursor = Cursors.WaitCursor
DirSearch("\10.0.15.87\c$")
Cursor = Cursors.Default
End Sub
Public Sub DirSearch(ByVal sDir As String)
Try
For Each dir As String In Directory.GetDirectories(sDir)
For Each file In Directory.GetFiles(dir, "myfile.extension")
MsgBox(file)
Next
DirSearch(dir)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
End Sub
当我运行代码:
我相信代码执行完成;光标变成 WaitCursor 大约半秒钟,然后恢复正常,此时不会抛出任何错误,一切都将继续正常进行。如果我将“10.0.15.87”更改为“10.0.15.81”(我已经手动对其进行身份验证),那么我会弹出一些带有文件路径的 MsgBox。 (我确定问题不在于搜索文件,我知道它适用于其他 ip,并且我正在搜索的文件位于 10.0.15.87 的 c 驱动器中 - 我相信我只需要通过身份验证即可。 )
这是我尝试手动浏览到 ip 87 时发生的情况:
关于 10.0.15.87 的信息:
- 与我 运行 从中获取代码的计算机不同,它没有连接到域。 (两者都不是 10.0.15.81)
- 它有一个静态ip
- 如果我手动尝试访问它,我可以按照上图输入凭据,我只需要通过代码传递凭据。
如何在尝试访问服务器之前通过身份验证?
更新:我相信我已经找到了最简单和最一致的方法,方法 2 和 3 可能会或可能不会始终如一地工作。
方法 1 - 调用 Net Use(一致且跨域工作)
Net Use Command 允许您通过命令提示符将凭据存储到共享,并且您可以非常轻松地从 .NET 调用它。
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim proc As New Process
proc.StartInfo.FileName = "net"
proc.StartInfo.UseShellExecute = True
proc.EnableRaisingEvents = False
Dim server As String = "Server"
Dim user As String = "User"
Dim pass As String = "Pass"
If EnsureConnection(server) Then
proc.StartInfo.Arguments = "use \" & server & "\IPC$ /u:" & server & "\" & user & " " & pass
proc.Start()
End If
End Sub
Function EnsureConnection(server As String)
'Give more or less ping attempts depending on how reliable your connection is.
'I have found that one ping can give false negative easily even on reliable connections
If My.Computer.Network.Ping(server) Then
Return True
ElseIf My.Computer.Network.Ping(server) Then
Return True
Else
Return False
End If
End Function
此方法会将凭据存储在机器上,直到机器关闭或重新启动,并且用户也将在程序之外进行身份验证。如果这不是您想要的,您可以随时使用命令的“/delete”开关在程序完成后立即将其删除。
方法 2 - 使用 MPR.DLL
中的函数我终于找到了this answer另一个问题。这是对我有用的方法。这就是它集成到我的项目中的样子:
Imports System.IO
Imports System.Runtime.InteropServices
Public Class Form1
Dim user As String
Dim pass As String
Dim path As String
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Cursor = Cursors.WaitCursor
user = "UsernameString"
pass = "PasswordString"
path = "\10.0.15.87\c$"
MPRAuth()
DirSearch(path)
Cursor = Cursors.Default
End Sub
Private Sub MPRAuth()
Dim nr As New NETRESOURCE
nr.dwType = RESOURCETYPE_DISK
nr.lpRemoteName = path
If WNetAddConnection2(nr, pass, user, 0) <> NO_ERROR Then
Throw New Exception("WNetAddConnection2 failed.")
End If
If WNetCancelConnection2(path, 0, True) <> NO_ERROR Then
Throw New Exception("WNetCancelConnection2 failed.")
End If
End Sub
Public Sub DirSearch(ByVal sDir As String)
Try
For Each dir As String In Directory.GetDirectories(sDir)
Try
For Each file In Directory.GetFiles(dir, "*.exe")
MsgBox(file)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
DirSearch(dir)
Next
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
End Sub
<StructLayout(LayoutKind.Sequential)>
Private Structure NETRESOURCE
Public dwScope As UInteger
Public dwType As UInteger
Public dwDisplayType As UInteger
Public dwUsage As UInteger
<MarshalAs(UnmanagedType.LPTStr)>
Public lpLocalName As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpRemoteName As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpComment As String
<MarshalAs(UnmanagedType.LPTStr)>
Public lpProvider As String
End Structure
Private Const NO_ERROR As UInteger = 0
Private Const RESOURCETYPE_DISK As UInteger = 1
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetAddConnection2(ByRef lpNetResource As NETRESOURCE, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpPassword As String, <[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpUserName As String, ByVal dwFlags As UInteger) As UInteger
End Function
<DllImport("mpr.dll", CharSet:=CharSet.Auto)>
Private Shared Function WNetCancelConnection2(<[In](), MarshalAs(UnmanagedType.LPTStr)> ByVal lpName As String, ByVal dwFlags As UInteger, <MarshalAs(UnmanagedType.Bool)> ByVal fForce As Boolean) As UInteger
End Function
End Class
方法 3 - NetworkCredential 和 CredentialCache
此方法将 ip 转换为 URI 并缓存凭据。 我无法在与我不同域的服务器上使用此方法。
Imports System.Net
Dim builder As New UriBuilder("10.0.15.87")
Dim uri As Uri = builder.Uri
Dim netCred = New NetworkCredential("UsernameString", "PasswordString", "DomainString")
Dim netCache = New CredentialCache()
netCache.Add(uri, "Basic", netCred)
DirSearch("\10.0.15.87\c$")
方法 2 比方法 2 简单得多,只要我尝试连接的服务器与我在同一个域中,它对我来说就可以正常工作。当我尝试连接到不在域中的服务器时,我收到错误 'User name or password is incorrect',这不是真的,所以我不确定问题出在哪里 - 也许其他人知道。到目前为止,方法 2 对我来说对任何服务器都完美无缺,无论域是什么,或者是否在 运行 代码之前对其进行了手动身份验证。希望这对其他人有帮助!