使用 PrincipalContext.ValidateCredentials 对本地计算机进行身份验证时出错?

Posted

技术标签:

【中文标题】使用 PrincipalContext.ValidateCredentials 对本地计算机进行身份验证时出错?【英文标题】:Error when using PrincipalContext.ValidateCredentials to authenticate against a Local Machine? 【发布时间】:2013-03-15 09:02:38 【问题描述】:

我有一个 WCF 服务,其中包含一个 Login 方法,该方法根据本地计算机凭据验证用户名和密码,并且在看似随机的一段时间后它将停止对某些用户工作。

实际的登录命令如下所示:

public UserModel Login(string username, string password, string ipAddress)

    // Verify valid parameters
    if (username == null || password == null)
        return null;

    try
    
        using (var pContext = new PrincipalContext(ContextType.Machine))
        
            // Authenticate against local machine
            if (pContext.ValidateCredentials(username, password))
            
                // Authenticate user against database
                using (var context = new MyEntities(Connections.GetConnectionString()))
                
                    var user = (from u in context.Users
                                where u.LoginName.ToUpper() == username.ToUpper()
                                      && u.IsActive == true
                                      && (string.IsNullOrEmpty(u.AllowedIpAddresses)
                                        || u.AllowedIpAddresses.Contains(ipAddress))
                                select u).FirstOrDefault();

                    // If user failed to authenticate against database
                    if (user == null)
                        return null;

                    // Map entity object to return object and assign session token
                    var userModel = Mapper.Map<User, UserModel>(user);
                    userModel.Token = Guid.NewGuid();
                    userModel.LastActivity = DateTime.Now;

                    // Authenticated users are added to a list on the server
                    // and their login expires after 20 minutes of inactivity
                    authenticatedUsers.Add(userModel);
                    sessionTimer.Start();

                    // User successfully authenticated, so return UserModel to client
                    return userModel;
                
            
        
    
    catch(Exception ex)
    
        // This is getting hit
        MessageLog.WriteMessage(string.Format("Exception occurred while validating user: 0\n1", ex.Message, ex.StackTrace));
        return null;
    

    // If authentication against local machine failed, return null
    return null;

这似乎可以正常工作几天,然后它会突然停止对某些用户工作并抛出此异常:

不允许同一用户使用多个用户名与服务器或共享资源建立多个连接。断开与服务器或共享资源的所有先前连接,然后重试。 (HRESULT 异常:0x800704C3)

在 System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(字符串目标,字符串用户名,字符串密码)

在 System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)

在 System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(字符串用户名,字符串密码)

在 C:\Users\me\Desktop\somefolder\LoginService.svc.cs:line 67 中的 MyNamespace.LoginService.Login(String username, String password, String ipAddress)

第 67 行是:if (pContext.ValidateCredentials(username, password))

我不确定它是否重要,但错误消息的最后一行是我的开发机器上 VS 解决方案的路径,而不是生产服务器上文件的路径。

当它失败时,它只会对某些用户失败,而其他用户可以继续登录就好了。我发现暂时修复错误的唯一方法是运行iisreset。停止/启动网站或回收应用程序池不起作用。

我无法按需重现错误。我曾尝试使用来自多个会话和 IP 地址的同一用户登录,同时使用来自同一浏览器会话的不同用户登录,向“登录”按钮发送垃圾邮件以尝试让它运行多次,等等,但一切都出现了工作正常。

我可以从我们的日志中看到用户过去已经成功登录:

2013 年 3 月 21 日 o 9:03a 我登录了 o 1:54p 用户 B 登录 o 1:55p 用户 A 登录 o 2:38p 用户 B 登录 o 3:48p 用户 B 登录 o 5:18p 用户 A 登录 o 6:11p 用户 B 登录 2013 年 3 月 22 日 o 12:42p 用户 A 登录 o 5:22p 用户 B 登录 o 8:04p 用户 B 登录 2013 年 3 月 25 日(今天) o 8:47a 我登录了 o 12:38p UserB 尝试登录但失败。在接下来的 45 分钟内重复约 15 次 o 1:58p 我登录成功 o 2:08p 我尝试使用 UserB 的登录信息登录,但由于相同的错误而失败

我们针对本地计算机进行身份验证的原因是因为用户在本地创建了一个用于 FTP 访问的帐户,并且我们不想构建自己的自定义登录系统或让我们的用户记住两组凭据。

代码应该只验证用户的凭据,并且不对用户的凭据做任何其他事情。没有其他代码使用System.DirectoryServices,没有文件 IO 正在进行,并且除了运行 Web 应用程序所需的文件之外,无法访问文件系统上的任何本地内容。

几天后,什么会导致该错误看似随机出现?我该如何解决?

服务器是 Windows Server 2003,运行 IIS 6.0,并设置为使用 .Net Framework 4.0

【问题讨论】:

【参考方案1】:

我可以在网上找到最接近解释此问题的方法是 this forum post,其中用户遇到了同样的错误并得到了重播说明:

WinNT 提供程序在服务器环境中表现不佳。我是 实际上很惊讶你看不到这个负载要小得多。我有 只需要 2 或 3 个用户就可以做到这一点。

和this SO comment 声明

正确验证某人的最佳方法是使用 LogonUserAPI 正如@stephbu 所写。本文中描述的所有其他方法都不会 工作 100%

其中“所有其他方法”包括使用PrincipalContext.ValidateCredentials 的最高投票答案

听起来PrincipalContext.ValidateCredentials 在 Windows Server 2003 和 IIS6.0 上并非完全 100% 可靠,因此我重写了我的身份验证代码以改用 LogonUser WinAPI 方法。

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    out IntPtr phToken
    );

IntPtr hToken;
if (LogonUser(username, "", password, 
    LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, out hToken))

    ...

【讨论】:

以上是关于使用 PrincipalContext.ValidateCredentials 对本地计算机进行身份验证时出错?的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)