使用PrincipalContext时出错.validatecredential根据本地机器进行身份验证

本文关键字:机器 身份验证 PrincipalContext 出错 validatecredential 使用 | 更新日期: 2023-09-27 18:05:39

我有一个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}'n{1}", ex.Message, ex.StackTrace));
        return null;
    }
    // If authentication against local machine failed, return null
    return null;
}

这在几天内似乎可以正常工作,然后它会突然停止工作,并抛出以下异常:

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

在System.DirectoryServices.AccountManagement.CredentialValidator

。BindSam(字符串目标,字符串用户名,字符串密码)

在System.DirectoryServices.AccountManagement.CredentialValidator

。验证(字符串userName,字符串password)

在System.DirectoryServices.AccountManagement.PrincipalContext

。validatecredals (String userName, String password)

在MyNamespace.LoginService

。Login(String username, String password, String ipAddress) in C:'Users'me'Desktop'somefolder'LoginService.svc.cs:line 67

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

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

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

我无法按要求重现错误。我试过从多个会话和IP地址登录同一用户,从同一浏览器会话登录不同的用户在同一时间,垃圾邮件登录按钮,试图让它运行多次,等,但一切似乎都很好。

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

<>之前3/21/20139:03 . a我登录了0 1:54p用户b登录0 1:55p用户登录o 2:38p用户b登录o 3:48p用户b登录5点18分用户登录o 6:11p用户b登录3/22/2013[12:42 . 02]用户登录o 5:22p用户b登录08:04 p用户b登录3/25/2013(今天)8:47a我登录了o 12:38p用户b尝试登录,失败。在接下来的45分钟内重复~15次1:58我登录成功0 2:08p我尝试登录与用户b的登录信息,失败与同样的错误之前

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

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

是什么导致这个错误在几天后出现,似乎是随机的?我该怎么补救呢?

服务器是Windows server 2003,运行IIS 6.0,并设置为使用。net Framework 4.0

使用PrincipalContext时出错.validatecredential根据本地机器进行身份验证

我能在网上找到最接近解释这个问题的是这个论坛帖子,用户遇到同样的错误,并得到重播声明:

WinNT提供程序在服务器环境中不能很好地工作。我是实际上很惊讶你没有在更小的负载上看到这个。我有只有2到3个用户就能得到这个。

和这个SO注释说明

正确验证某人的最佳方法是使用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))
{
    ...
}