首页 文章

验证Active Directory的用户名和密码?

提问于
浏览
475

如何针对Active Directory验证用户名和密码?我只是想检查用户名和密码是否正确 .

12 回答

  • 11

    我的简单功能

    private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
        {
            try
            {
                DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
                DirectorySearcher ds = new DirectorySearcher(de);
                ds.FindOne();
                return true;
            }
            catch //(Exception ex)
            {
                return false;
            }
        }
    
  • 592

    此处介绍的几种解决方案无法区分错误的用户/密码和需要更改的密码 . 这可以通过以下方式完成:

    using System;
    using System.DirectoryServices.Protocols;
    using System.Net;
    
    namespace ProtocolTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
                    NetworkCredential credential = new NetworkCredential("user", "password");
                    connection.Credential = credential;
                    connection.Bind();
                    Console.WriteLine("logged in");
                }
                catch (LdapException lexc)
                {
                    String error = lexc.ServerErrorMessage;
                    Console.WriteLine(lexc);
                }
                catch (Exception exc)
                {
                    Console.WriteLine(exc);
                }
            }
        }
    }
    

    如果用户密码错误,或者用户不存在,则会包含错误

    “8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext错误,数据52e,v1db1”,

    如果需要更改用户密码,它将包含

    “8009030C:LdapErr:DSID-0C0904DC,注释:AcceptSecurityContext错误,数据773,v1db1”

    lexc.ServerErrorMessage 数据值是Win32错误代码的十六进制表示形式 . 这些是相同的错误代码,否则将通过调用Win32 LogonUser API调用返回 . 下面的列表总结了一系列带有十六进制和十进制值的常见值:

    525​ user not found ​(1317)
    52e​ invalid credentials ​(1326)
    530​ not permitted to logon at this time​ (1328)
    531​ not permitted to logon at this workstation​ (1329)
    532​ password expired ​(1330)
    533​ account disabled ​(1331) 
    701​ account expired ​(1793)
    773​ user must reset password (1907)
    775​ user account locked (1909)
    
  • 62

    使用DirectoryServices的非常简单的解决方案:

    using System.DirectoryServices;
    
    //srvr = ldap server, e.g. LDAP://domain.com
    //usr = user name
    //pwd = user password
    public bool IsAuthenticated(string srvr, string usr, string pwd)
    {
        bool authenticated = false;
    
        try
        {
            DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
            object nativeObject = entry.NativeObject;
            authenticated = true;
        }
        catch (DirectoryServicesCOMException cex)
        {
            //not authenticated; reason why is in cex
        }
        catch (Exception ex)
        {
            //not authenticated due to some other exception [this is optional]
        }
    
        return authenticated;
    }
    

    需要NativeObject访问才能检测到错误的用户/密码

  • 48

    另一个.NET调用快速验证LDAP凭据:

    using System.DirectoryServices;
    
    using(var DE = new DirectoryEntry(path, username, password)
    {
        try
        {
            DE.RefreshCache(); // This will force credentials validation
        }
        catch (COMException ex)
        {
            // Validation failed - handle how you want
        }
    }
    
  • 0

    我们在内联网上这样做

    你必须使用System.DirectoryServices;

    以下是代码的内容

    using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
    {
        using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
        {
            //adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
            adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";
    
            try
            {
                SearchResult adsSearchResult = adsSearcher.FindOne();
                bSucceeded = true;
    
                strAuthenticatedBy = "Active Directory";
                strError = "User has been authenticated by Active Directory.";
            }
            catch (Exception ex)
            {
                // Failed to authenticate. Most likely it is caused by unknown user
                // id or bad strPassword.
                strError = ex.Message;
            }
            finally
            {
                adsEntry.Close();
            }
        }
    }
    
  • 21

    如果您使用的是.NET 3.5或更高版本,则可以使用 System.DirectoryServices.AccountManagement 命名空间并轻松验证您的凭据:

    // create a "principal context" - e.g. your domain (could be machine, too)
    using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
    {
        // validate the credentials
        bool isValid = pc.ValidateCredentials("myuser", "mypassword");
    }
    

    它很简单,可靠,它是你的100%C#托管代码 - 你还能要求什么? :-)

    在这里阅读所有相关内容:

    Update:

    this other SO question (and its answers)中所述,此调用可能会返回 True 用于用户的旧密码 . 只要注意这种行为,如果发生这种情况就不要太惊讶:-)(感谢@MikeGledhill指出这一点!)

  • 17

    遗憾的是,没有“简单”的方法来检查AD上的用户凭据 .

    对于目前为止提出的每种方法,您可能会得到假阴性:用户的信用证有效,但在某些情况下AD会返回false:

    • 用户需要在下次登录时更改密码 .

    • 用户密码已过期 .

    ActiveDirectory不允许您使用LDAP来确定密码是否无效,因为用户必须更改密码或密码已过期 .

    要确定密码更改或密码已过期,您可以调用Win32:LogonUser(),并检查以下2个常量的Windows错误代码:

    • ERROR_PASSWORD_MUST_CHANGE = 1907

    • ERROR_PASSWORD_EXPIRED = 1330

  • 32

    如果你坚持使用.NET 2.0和托管代码,这是另一种适用于本地和域帐户的方法:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Security;
    using System.Diagnostics;
    
    static public bool Validate(string domain, string username, string password)
    {
        try
        {
            Process proc = new Process();
            proc.StartInfo = new ProcessStartInfo()
            {
                FileName = "no_matter.xyz",
                CreateNoWindow = true,
                WindowStyle = ProcessWindowStyle.Hidden,
                WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                UseShellExecute = false,
                RedirectStandardError = true,
                RedirectStandardOutput = true,
                RedirectStandardInput = true,
                LoadUserProfile = true,
                Domain = String.IsNullOrEmpty(domain) ? "" : domain,
                UserName = username,
                Password = Credentials.ToSecureString(password)
            };
            proc.Start();
            proc.WaitForExit();
        }
        catch (System.ComponentModel.Win32Exception ex)
        {
            switch (ex.NativeErrorCode)
            {
                case 1326: return false;
                case 2: return true;
                default: throw ex;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    
        return false;
    }
    
  • 10

    试试这段代码(注意:报告不能在Windows Server 2000上运行)

    #region NTLogonUser
    #region Direct OS LogonUser Code
    [DllImport( "advapi32.dll")]
    private static extern bool LogonUser(String lpszUsername, 
        String lpszDomain, String lpszPassword, int dwLogonType, 
        int dwLogonProvider, out int phToken);
    
    [DllImport("Kernel32.dll")]
    private static extern int GetLastError();
    
    public static bool LogOnXP(String sDomain, String sUser, String sPassword)
    {
       int token1, ret;
       int attmpts = 0;
    
       bool LoggedOn = false;
    
       while (!LoggedOn && attmpts < 2)
       {
          LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
          if (LoggedOn) return (true);
          else
          {
             switch (ret = GetLastError())
             {
                case (126): ; 
                   if (attmpts++ > 2)
                      throw new LogonException(
                          "Specified module could not be found. error code: " + 
                          ret.ToString());
                   break;
    
                case (1314): 
                   throw new LogonException(
                      "Specified module could not be found. error code: " + 
                          ret.ToString());
    
                case (1326): 
                   // edited out based on comment
                   //  throw new LogonException(
                   //   "Unknown user name or bad password.");
                return false;
    
                default: 
                   throw new LogonException(
                      "Unexpected Logon Failure. Contact Administrator");
                  }
              }
           }
       return(false);
    }
    #endregion Direct Logon Code
    #endregion NTLogonUser
    

    除了你需要为“LogonException”创建自己的自定义异常

  • 1

    完整的.Net解决方案是使用System.DirectoryServices命名空间中的类 . 它们允许直接查询AD服务器 . 这是一个小样本,可以这样做:

    using (DirectoryEntry entry = new DirectoryEntry())
    {
        entry.Username = "here goes the username you want to validate";
        entry.Password = "here goes the password";
    
        DirectorySearcher searcher = new DirectorySearcher(entry);
    
        searcher.Filter = "(objectclass=user)";
    
        try
        {
            searcher.FindOne();
        }
        catch (COMException ex)
        {
            if (ex.ErrorCode == -2147023570)
            {
                // Login or password is incorrect
            }
        }
    }
    
    // FindOne() didn't throw, the credentials are correct
    

    此代码使用提供的凭据直接连接到AD服务器 . 如果凭据无效,searcher.FindOne()将抛出异常 . ErrorCode是与“无效的用户名/密码”COM错误相对应的错误 .

    您不需要以AD用户身份运行代码 . 事实上,我成功地使用它来从域外的客户端查询AD服务器上的信息!

  • 28

    可能最简单的方法是PInvoke LogonUser Win32 API.e.g.

    http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html

    MSDN参考这里......

    http://msdn.microsoft.com/en-us/library/aa378184.aspx

    绝对要使用登录类型

    LOGON32_LOGON_NETWORK (3)
    

    这仅创建一个轻量级令牌 - 非常适合AuthN检查 . (其他类型可用于构建交互式会话等)

  • 5

    Windows身份验证可能由于各种原因而失败:错误的用户名或密码,锁定的帐户,过期的密码等 . 要区分这些错误,请通过P / Invoke调用LogonUser API函数,如果函数返回 false ,则检查错误代码:

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    
    using Microsoft.Win32.SafeHandles;
    
    public static class Win32Authentication
    {
        private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle() // called by P/Invoke
                : base(true)
            {
            }
    
            protected override bool ReleaseHandle()
            {
                return CloseHandle(this.handle);
            }
        }
    
        private enum LogonType : uint
        {
            Network = 3, // LOGON32_LOGON_NETWORK
        }
    
        private enum LogonProvider : uint
        {
            WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
        }
    
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);
    
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(
            string userName, string domain, string password,
            LogonType logonType, LogonProvider logonProvider,
            out SafeTokenHandle token);
    
        public static void AuthenticateUser(string userName, string password)
        {
            string domain = null;
            string[] parts = userName.Split('\\');
            if (parts.Length == 2)
            {
                domain = parts[0];
                userName = parts[1];
            }
    
            SafeTokenHandle token;
            if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
                token.Dispose();
            else
                throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
        }
    }
    

    样品用法:

    try
    {
        Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
        // Or: Win32Authentication.AuthenticateUser("user@example.com", "P@ssw0rd");
    }
    catch (Win32Exception ex)
    {
        switch (ex.NativeErrorCode)
        {
            case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
                // ...
            case 1327: // ERROR_ACCOUNT_RESTRICTION
                // ...
            case 1330: // ERROR_PASSWORD_EXPIRED
                // ...
            case 1331: // ERROR_ACCOUNT_DISABLED
                // ...
            case 1907: // ERROR_PASSWORD_MUST_CHANGE
                // ...
            case 1909: // ERROR_ACCOUNT_LOCKED_OUT
                // ...
            default: // Other
                break;
        }
    }
    

    注意:LogonUser需要与您要验证的域之间存在信任关系 .

相关问题