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

时间:2013-03-25 18:39:56

标签: c# wcf authentication active-directory principalcontext

我有一个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;
}

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

  

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

     

at System.DirectoryServices.AccountManagement.CredentialValidator.BindSam(String target,String userName,String password)

     

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

     

at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName,String password)

     

at MyNamespace.LoginService.Login(String username,String password,String ipAddress)在C:\ Users \ me \ Desktop \ somefolder \ LoginService.svc.cs:第67行

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

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

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

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

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

3/21/2013
o   9:03a   I logged in
o   1:54p   UserB logged in
o   1:55p   UserA logged in
o   2:38p   UserB logged in
o   3:48p   UserB logged in
o   5:18p   UserA logged in
o   6:11p   UserB logged in

3/22/2013
o   12:42p  UserA logged in
o   5:22p   UserB logged in
o   8:04p   UserB logged in

3/25/2013 (today)
o   8:47a   I logged in
o   12:38p  UserB tries logging in and fails. Repeated ~15 times over next 45 min
o   1:58p   I login successfully
o   2:08p   I try to login with UserB's login info and fail with the same error

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

代码应仅验证用户的凭据,并且不会对用户的凭据执行任何其他操作。没有其他代码使用System.DirectoryServices,没有文件IO进行,并且除了运行Web应用程序所需的文件之外,无法访问文件系统本地的任何内容。

在几天之后,什么可能导致该错误似乎随机出现?我该如何解决?

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

1 个答案:

答案 0 :(得分:4)

我在网上找到的最接近解释此问题的是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))
{
    ...
}