【问题标题】:Validate user credentials against domain controller in .net在 .net 中针对域控制器验证用户凭据
【发布时间】:2012-02-07 14:16:37
【问题描述】:

在 .NET 应用程序中,我试图通过用户名和密码对 Windows 用户、本地用户以及域用户进行身份验证。 I already tried this solution 。我获取 PrincipalContext 的代码如下所示:

protected static PrincipalContext TryCreatePrincipalContext(String domain)
{
    var computerDomain = TryGetComputerDomain();

    if (String.IsNullOrEmpty(domain) && String.IsNullOrEmpty(computerDomain))
        return new PrincipalContext(ContextType.Machine);
    else if (String.IsNullOrEmpty(domain))
        return new PrincipalContext(ContextType.Domain, computerDomain);
    else
        return new PrincipalContext(ContextType.Domain, domain);
}

protected static String TryGetComputerDomain()
{
    try
    {
        var domain = Domain.GetComputerDomain();
        return domain.Name;
    } catch
    {
       return null;
    }
}

这适用于本地 Windows 用户和 ActiveDirectory 中的远程用户。但是,如果我尝试在一台机器上运行身份验证,则该机器已加入非 ActiveDirectory 域主机,例如。一个 Samba 服务器我得到以下异常:

System.DirectoryServices.AccountManagement.PrincipalServerDownException: Mit dem Server konnte keine Verbindung hergestellt werden. ---> 
System.DirectoryServices.Protocols.LdapException: Der LDAP-Server ist nicht verfügbar.
bei System.DirectoryServices.Protocols.LdapConnection.Connect()
bei System.DirectoryServices.Protocols.LdapConnection.SendRequestHelper(DirectoryRequest request, Int32& messageID)
bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request, TimeSpan requestTimeout)
bei System.DirectoryServices.Protocols.LdapConnection.SendRequest(DirectoryRequest request)
bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)
--- Ende der internen Ausnahmestapelüberwachung ---
bei System.DirectoryServices.AccountManagement.PrincipalContext.ReadServerConfig(String serverName, ServerProperties& properties)
bei System.DirectoryServices.AccountManagement.PrincipalContext.DoServerVerifyAndPropRetrieval()
bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name, String container, ContextOptions options, String userName, String password)
bei System.DirectoryServices.AccountManagement.PrincipalContext..ctor(ContextType contextType, String name)
bei DomainAuthTest.DomainAuthenticator.TryCreatePrincipalContext(String domain)
bei DomainAuthTest.DomainAuthenticator.Authenticate(String domainUser, String  password)
bei DomainAuthTest.Program.Main(String[] args)

因此,在 ContextType.Domain 的情况下,PrincipalContext 似乎尝试使用 LDAP。如果我尝试使用 ContextType.Machine 我无法使用工作组/域名,因为 PrincipalContext 尝试直接连接到机器。如果已经有来自同一台计算机的窗口与该计算机的连接,则该操作失败。

所以我的问题是:

  • 如何根据域主服务器(不一定基于 ActiveDirectory)使用凭据域、用户名和密码对用户进行身份验证?
  • 是否有用于完成上述任务的托管 API?
  • 如果没有托管的基础课程,那么正确的方向是什么?

感谢您的回复。

【问题讨论】:

    标签: c# .net windows winapi authentication


    【解决方案1】:

    为了完整起见,这里我的解决方案似乎完全符合我的要求:

    public class WinApiDomainAuthenticator
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(string lpszUsername,
                                            string lpszDomain,
                                            string lpszPassword,
                                            int dwLogonType,
                                            int dwLogonProvider,
                                            out IntPtr phToken);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);
    
        public static IPrincipal Authenticate(String domainUser, String password)
        {
            var userToken = IntPtr.Zero;
            var creds = new DomainAuthCredentials(domainUser, password); 
    
            if (! LogonUser(creds.Username, 
                            creds.Domain,
                            creds.Password,
                           (int)LogonType.LOGON32_LOGON_BATCH, 
                           (int)LogonProvider.LOGON32_PROVIDER_DEFAULT, out userToken))
            {
                var error = new Win32Exception(Marshal.GetLastWin32Error());
                throw new SecurityException("Error while authenticating user", error);
            }
    
            var identity = new WindowsIdentity(userToken);
    
            if (userToken != IntPtr.Zero) 
                CloseHandle(userToken);
    
            return ConvertWindowsIdentityToGenericPrincipal(identity);
        }
    
        protected static IPrincipal ConvertWindowsIdentityToGenericPrincipal(WindowsIdentity windowsIdentity)
        {
            if (windowsIdentity == null)
                return null;
    
            // Identity in format DOMAIN\Username
            var identity = new GenericIdentity(windowsIdentity.Name);
    
            var groupNames = new string[0];
            if (windowsIdentity.Groups != null)
            {
                // Array of Group-Names in format DOMAIN\Group
                groupNames = windowsIdentity.Groups
                                            .Select(gId => gId.Translate(typeof(NTAccount)))
                                            .Select(gNt => gNt.ToString())
                                            .ToArray();
            }
    
            var genericPrincipal = new GenericPrincipal(identity, groupNames);
            return genericPrincipal;
        }
    
        protected class DomainAuthCredentials
        {
            public DomainAuthCredentials(String domainUser, String password)
            {
                Username = domainUser;
                Password = password;
                Domain = ".";
    
                if (!domainUser.Contains(@"\"))
                    return;
    
                var tokens = domainUser.Split(new char[] { '\\' }, 2);
                Domain = tokens[0];
                Username = tokens[1];
            }
    
            public DomainAuthCredentials()
            {
                Domain = String.Empty;
            }
    
            #region Properties
    
            public String Domain { get; set; }
            public String Username { get; set; }
            public String Password { get; set; }
    
            #endregion
        }
    }
    

    LogonType 和 LogonProvider 枚举反映了“Winbase.h”中的定义。我选择了 LogonType.LOGON32_LOGON_BATCH 而不是 LogonType.LOGON32_LOGON_NETWORK,因为 samba 3.4.X 似乎对这种类型有问题。

    【讨论】:

      【解决方案2】:

      这是我刚刚为自己正在开发的应用程序所做的一个 - 需要 Framework v3.5 或更高版本....

      public static bool Authenticate(string user, string password)
      {
          // Split the user name in case a domain name was specified as DOMAIN\USER
          string[] NamesArray = user.Split(new char[] { '\\' }, 2);
      
          // Default vars for names & principal context type
          string DomainName = string.Empty;
          string UserName = string.Empty;
          ContextType TypeValue = ContextType.Domain;
      
          // Domain name was supplied
          if (NamesArray.Length > 1)
          {
              DomainName = NamesArray[0];
              UserName = NamesArray[1];
          }
          else
          {
              // Pull domain name from environment
              DomainName = Environment.UserDomainName;
              UserName = user;
      
              // Check this against the machine name to pick up on a workgroup
              if (string.Compare(DomainName, System.Environment.MachineName, StringComparison.InvariantCultureIgnoreCase) == 0)
              {
                  // Use the domain name as machine name (local user)
                  TypeValue = ContextType.Machine;
              }
          }
      
          // Create the temp context
          using (PrincipalContext ContextObject = new PrincipalContext(TypeValue, DomainName))
          {
              // Validate the credentials
              return ContextObject.ValidateCredentials(UserName, password);
          }
      }
      

      【讨论】:

      • 感谢大天使的回复。不幸的是,这并不能解决问题。您针对当前目录服务器进行身份验证。恕我直言,System.DirectoryServices.AccountManagement.PrincipalContext 类不起作用,如果机器加入了没有 Active-Directory 的 WIN2000/Samba 域,或者即使它没有加入任何域。 “advapi32.dll”中的“LogonUser”方法对于这些情况都是透明的。
      猜你喜欢
      • 1970-01-01
      • 2021-09-27
      • 1970-01-01
      • 1970-01-01
      • 2013-02-15
      • 1970-01-01
      • 2010-09-24
      • 2011-10-30
      • 1970-01-01
      相关资源
      最近更新 更多