我有一个运行在Novellnetworking上的WinForms客户端服务器应用程序,当连接到networking上的独立Windows 2003 Server时,产生以下错误:
TYPE: System.IO.IOException MSG: Logon failure: unknown user name or bad password. SOURCE: mscorlib SITE: WinIOError at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) at System.IO.Directory.InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, Boolean includeFiles, Boolean includeDirs, SearchOption searchOption) at System.IO.DirectoryInfo.GetFiles(String searchPattern, SearchOption searchOption) at System.IO.DirectoryInfo.GetFiles(String searchPattern) at Ceoimage.Basecamp.DocumentServers.ClientAccessServer.SendQueuedFiles( Int32 queueId, Int32 userId, IDocQueueFile[] queueFiles) at Ceoimage.Basecamp.ScanDocuments.DataModule.CommitDocumentToQueue( QueuedDocumentModelWithCollections doc, IDocQueueFile[] files)
客户的networkingpipe理员通过手动同步工作站用户名和密码与服务器上的本地用户来pipe理Windows Server连接。 关于错误的奇怪之处在于,用户可以在错误之前和之后写入服务器,而不用明确地login。
你能解释为什么发生错误,并提供解决scheme?
尝试访问不同域中的Windows服务器的文件系统时遇到同样的问题。 问题是程序正在运行的用户帐户无权访问远程服务器。 Windows使用Windows资源管理器在幕后做了额外的工作,使其看起来无缝,因为它猜测您的远程凭证将与您的本地凭证匹配。
如果将驱动器本地映射到远程服务器,则在代码中使用本地映射的驱动器,则不会出现问题。 如果您无法映射驱动器,但是可以硬编码用于远程服务器的凭据,则可以使用以下代码:
using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security.Principal; namespace Company.Security { public class ImpersonateUser : IDisposable { [DllImport("advapi32.dll", SetLastError=true)] private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); [DllImport( "kernel32", SetLastError = true )] private static extern bool CloseHandle(IntPtr hObject); private IntPtr userHandle = IntPtr.Zero; private WindowsImpersonationContext impersonationContext; public ImpersonateUser( string user, string domain, string password ) { if ( ! string.IsNullOrEmpty( user ) ) { // Call LogonUser to get a token for the user bool loggedOn = LogonUser( user, domain, password, 9 /*(int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS*/, 3 /*(int)LogonProvider.LOGON32_PROVIDER_WINNT50*/, out userHandle ); if ( !loggedOn ) throw new Win32Exception( Marshal.GetLastWin32Error() ); // Begin impersonating the user impersonationContext = WindowsIdentity.Impersonate( userHandle ); } } public void Dispose() { if ( userHandle != IntPtr.Zero ) CloseHandle( userHandle ); if ( impersonationContext != null ) impersonationContext.Undo(); } } }
然后你可以通过这样访问远程服务器:
using ( new ImpersonateUser( "UserID", "Domain", "Password" ) ) { // Any IO code within this block will be able to access the remote server. }
对于VB.Net开发人员(像我),这里是VB.Net版本:
Imports System Imports System.ComponentModel Imports System.Runtime.InteropServices Imports System.Security.Principal Namespace Company.Security Public Class ImpersonateUser Implements IDisposable <DllImport("advapi32.dll", SetLastError:=True)> _ Private Shared Function LogonUser(ByVal lpszUsername As String, ByVal lpszDomain As String, ByVal lpszPassword As String, ByVal dwLogonType As Integer, ByVal dwLogonProvider As Integer, ByRef phToken As IntPtr) As Integer End Function <DllImport("kernel32", SetLastError:=True)> _ Private Shared Function CloseHandle(ByVal hObject As IntPtr) As Boolean End Function Private userHandle As IntPtr = IntPtr.Zero Private impersonationContext As WindowsImpersonationContext Public Sub New(ByVal user As String, ByVal domain As String, ByVal password As String) If Not String.IsNullOrEmpty(user) Then Dim loggedOn As Integer = LogonUser(user, domain, password, 9, 3, userHandle) If Not loggedOn = 1 Then Throw New Win32Exception(Marshal.GetLastWin32Error()) End If impersonationContext = WindowsIdentity.Impersonate(userHandle) End If End Sub Public Sub Dispose() Implements System.IDisposable.Dispose If userHandle <> IntPtr.Zero Then CloseHandle(userHandle) End If If impersonationContext IsNot Nothing Then impersonationContext.Undo() End If End Sub End Class End Namespace
并使用它:
using New ImpersonateUser( "UserID", "Domain", "Password" ) ' ... your code here End Using
我认为你应该尝试重现这个问题,而不是使用数据包监视器来查看网络流量,并查看失败情况和成功情况之间的差异。
然后编写一个应用程序,使用窗口中的原始API(P / Invokes)重现您的失败情况,并尝试找到哪些参数导致错误发生。 如果你能够解决问题,而不是找到如何让组件去做你想要的事情的问题。
你可以看的其他方向(在你能稳定地重现问题之后):
恕我直言,这似乎是刷新过期的身份验证令牌(或类似的东西)的某种副作用。
我的情况下,作为一个Active Directory用户通过代理(鱿鱼)上网,我正在浏览没有问题,直到我得到(随机间隔)缺乏证书,这是由浏览器页面刷新解决的错误,那么一切正常工作,直到下一个错误。