我有一个在 Novell 网络上运行的 WinForms 客户端-服务器应用程序,当连接到网络上的单独 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)

客户的网络管理员通过手动将工作站用户名和密码与服务器上的本地用户同步来管理 Windows Server 连接。关于该错误的奇怪之处在于,用户可以在错误之前和之后向服务器写入数据,而无需显式登录。

您能解释一下为什么会出现错误并提供解决方案吗?

有帮助吗?

解决方案

试图访问一个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

我认为您应该尝试重现问题,而不是使用数据包监视器来查看网络流量并查看失败情况和成功情况之间的差异。

然后编写一个应用程序,使用 Windows 中的原始 api (P/Invokes) 来重现您的失败情况,并尝试查找哪些参数导致错误发生。如果您能够解决问题,那么只需找到如何让组件完成您想要的事情即可。

您可以查看的其他方向(在可以稳定地重现问题之后):

  • 使用 过程监控器 记录所有 api 调用并查看错误来自何处。
  • 在干净的虚拟机/机器上尝试并尝试在那里重现它
  • 禁用病毒扫描程序
  • 更新 Novell 客户端

IMHO,它似乎是某种刷新过期认证令牌(或类似的东西)的副作用。

我在我的情况下,具有通过代理(鱿鱼)上网,我浏览没有问题,直到我(在随机时间间隔)的错误对缺乏凭证,它是由一个页面刷新在解决一个Active Directory用户浏览器中,然后一切正常,直到下一个错误。

此地址有一个 Microsoft 示例: https://msdn.microsoft.com/en-us/library/system.security.principal.windowsimpersonationcontext(v=vs.110).aspx

但在他们的例子中确实说:“在 Windows Vista 及更高版本上,此示例必须以管理员身份运行。”

因此,仅当运行代码的用户在大多数平台上都是管理员时,此解决方案才有效。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top