【问题标题】:Validate a username and password against Active Directory?针对 Active Directory 验证用户名和密码?
【发布时间】:2010-09-22 09:03:23
【问题描述】:

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

【问题讨论】:

    标签: c# authentication active-directory


    【解决方案1】:

    如果您使用 .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# 托管代码 - 您还能要求什么? :-)

    在此处阅读所有相关信息:

    更新:

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

    【讨论】:

    • 在我的域中,我必须指定 pc.ValidateCredentials("myuser", "mypassword", ContextOptions.Negotiate) 否则我会得到 System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests .
    • 如果密码过期或帐户被禁用,则 ValidateCredentials 将返回 false。不幸的是,它并没有告诉你为什么它返回了假(这很遗憾,因为这意味着我不能做一些明智的事情,比如重定向用户以更改他们的密码)。
    • 还要注意“Guest”账户——如果启用了域级别的Guest账户,如果你给它一个不存在的用户,ValidateCredentials会返回true。因此,您可能需要先调用UserPrinciple.FindByIdentity 来查看传入的用户ID 是否存在。
    • @AlexPeck:你必须这样做的原因(像我一样)是 .NET 默认使用以下技术:LDAP+SSL、Kerberos,然后是 RPC。我怀疑 RPC 在您的网络中已关闭(很好!)并且 Kerberos 实际上并没有被 .NET 使用,除非您使用 ContextOptions.Negotiate 明确告诉它。
    • 请注意,如果用户更改了他们的 Active Directory 密码,这段代码将继续使用他们的旧 AD 密码对用户进行身份验证。是的,真的。在这里阅读:stackoverflow.com/questions/8949501/…
    【解决方案2】:

    我们在 Intranet 上执行此操作

    你必须使用 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();
            }
        }
    }
    

    【讨论】:

    • 你在“路径”中放了什么?域名的名称?服务器的名字?域的 LDAP 路径?服务器的 LDAP 路径?
    • Answer1:不,我们将它作为 Web 服务运行,因此可以从主 Web 应用程序中的多个位置调用它。答案 2:路径包含 LDAP 信息... LDAP://DC=domainname1,DC=domainname2,DC=com
    • 这似乎允许 LDAP 注入。您可能需要确保转义或删除 strAccountId 中的任何括号
    • 这是否意味着strPassword以纯文本形式存储在LDAP中?
    • 永远不需要在 using 变量上显式调用 Close()
    【解决方案3】:

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

    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)
    

    【讨论】:

    • 很遗憾,有些 AD 安装不返回 LDAP 子错误代码,这意味着该解决方案将不起作用。
    • 别忘了添加一些对项目的引用:System.DirectoryServicesSystem.DirectoryServices.Protocols
    • 不过,我的问题是:如何获取 LDAP 服务器名称?如果您正在编写一个可移植的应用程序,您不能期望用户知道或需要提供每个网络上的 AD 服务器的名称。
    • 我的用户只能登录特定的工作站;如何指定我尝试登录的工作站? (例如,workstation1 会因数据 531 而失败,workstation2 会正常工作)
    • 我觉得很奇怪,因为我认为您没有获得足够的信用。这是一种完全托管的方法,无需通过 Win32 API 调用来确定“用户必须重置密码”,这显然没有其他答案可以实现。这种方法是否存在漏洞导致其升值率低?嗯……
    【解决方案4】:

    使用 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 访问权限才能检测到错误的用户/密码

    【讨论】:

    • 这段代码很糟糕,因为它还在进行授权检查(检查是否允许用户读取活动目录信息)。用户名和密码可以是有效的,但不允许用户读取信息 - 并获得异常。换句话说,您可以拥有有效的用户名和密码,但仍然会出现异常。
    • 我实际上正在寻求与 PrincipleContext 等效的 native - 它仅存在于 .NET 3.5 中。但如果您使用的是 .NET 3.5 或更高版本,则应使用 PrincipleContext
    【解决方案5】:

    不幸的是,没有“简单”的方法来检查 AD 上的用户凭据。

    到目前为止,使用每种方法,您都可能会得到一个假阴性:用户的信用是有效的,但是在某些情况下 AD 会返回 false:

    • 用户需要在下次登录时更改密码。
    • 用户密码已过期。

    ActiveDirectory 将不允许您使用 LDAP 来确定密码是否由于用户必须更改密码或密码已过期而无效。

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

    • ERROR_PASSWORD_MUST_CHANGE = 1907
    • ERROR_PASSWORD_EXPIRED = 1330

    【讨论】:

    • 请问你从哪里得到 Expired 和 Must_Change 的设计...除了在这里找不到它们 :)
    • 谢谢。我试图找出我的验证一直返回错误的方式。这是因为用户需要更改他的密码。
    【解决方案6】:

    可能最简单的方法是 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 检查。 (其他类型可用于构建交互式会话等)

    【讨论】:

    • 正如@Alan 指出的那样,LogonUser API 除了 System.DirectoryServices 调用之外还有许多有用的特性。
    • @cciotti:不,那是错误的。正确验证某人的最佳方法是使用 LogonUserAPI 作为@stephbu 编写。本文中描述的所有其他方法都不会 100% 有效。不过请注意,我相信您必须加入域才能调用 LogonUser。
    • @Alan 要生成凭据,您必须能够通过提交有效的域帐户来连接到域。但是我很确定您的机器不一定需要成为域的成员。
    • LogonUser API 要求用户拥有 作为操作系统的一部分 权限;这不是用户得到的东西——也不是你想授予组织中的每个用户的东西。 (msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx)
    • LogonUser 根据support.microsoft.com/kb/180548 只需要 作为操作系统的一部分 用于 Windows 2000 及更低版本...对于 Server 2003 及更高版本看起来很干净。
    【解决方案7】:

    完整的 .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 对应于“invalid username/password” COM 错误。

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

    【讨论】:

    • 认证类型怎么样?我认为您在上面的代码中忘记了它。 :-) 默认情况下 DirectoryEntry.AuthenticationType 设置为 Secured 对吗?该代码不适用于不安全的 LDAP(可能是匿名或无)。我说得对吗?
    • 查询 AD 服务器的缺点是您有权限 来查询 AD 服务器。您的凭据可以是有效的,但如果您没有查询 AD 的权限,则会收到错误消息。这就是创建所谓的Fast Bind的原因;您在未授权用户执行某项操作的情况下验证凭据。
    • 如果在检查凭据之前由于任何其他原因引发 COMException,这是否允许任何人通过?
    【解决方案8】:

    又一个 .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
        }
    }
    

    【讨论】:

    • 这是唯一对我有用的解决方案,使用 PrincipalContext 对我没有用。
    • PrincipalContext 对于安全的 LDAP 连接(又名 LDAPS,使用端口 636)无效
    【解决方案9】:

    试试这个代码 (注意:据报道无法在 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”创建自己的自定义异常

    【讨论】:

    • 不要使用异常处理从方法返回信息。 “未知用户名或错误密码”并非例外,它是 LogonUser 的标准行为。只需返回 false。
    • 是的...这是一个来自旧的 VB6 库的端口...写于 2003 年左右...(当 .Net 首次出现时)
    • 如果在 Windows 2000 上运行,此代码将不起作用 (support.microsoft.com/kb/180548)
    • 重新思考这个问题。登录用户的预期行为,其目的,是登录用户。如果它未能执行该任务,它IS 是一个例外。实际上,该方法应该返回 void,而不是布尔值。另外,如果您只是返回一个布尔值,则方法的使用者无法告知用户失败的原因是什么。
    【解决方案10】:

    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 需要与您验证的域建立信任关系。

    【讨论】:

    • 你能解释一下为什么你的答案比投票最高的答案更好吗?
    • @MohammadAli:如果您需要知道凭据验证失败的原因(凭据不正确、帐户锁定、密码过期等),LogonUser API 函数会告诉您。相反, PrincipalContext.ValidateCredentials 方法(根据 marc_s 上的 cmets 的回答)不会;在所有这些情况下它都返回 false。另一方面,LogonUser 需要与域的信任关系,但 PrincipalContext.ValidateCredentials(我认为)不需要。
    【解决方案11】:

    如果您受困于 .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;
    }   
    

    【讨论】:

    • 适用于他启动脚本的机器的本地帐户
    • 顺便说一句,需要此方法才能使其正常工作 public static SecureString ToSecureString(string PwString) { char[] PasswordChars = PwString.ToCharArray(); SecureString 密码 = new SecureString(); foreach (char c in PasswordChars) Password.AppendChar(c); ProcessStartInfo foo = new ProcessStartInfo(); foo.Password = 密码;返回 foo.Password; }
    • 相反,无论如何都应该使用 SecureString 作为密码。 WPF PasswordBox 支持它。
    【解决方案12】:

    我的简单函数

     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;
            }
        }
    

    【讨论】:

      【解决方案13】:

      对我来说,以下这两种方法都有效,请确保您的域在开始时使用 LDAP:// 给出

      //"LDAP://" + domainName
      private void btnValidate_Click(object sender, RoutedEventArgs e)
      {
          try
          {
              DirectoryEntry de = new DirectoryEntry(txtDomainName.Text, txtUsername.Text, txtPassword.Text);
              DirectorySearcher dsearch = new DirectorySearcher(de);
              SearchResult results = null;
      
              results = dsearch.FindOne();
      
              MessageBox.Show("Validation Success.");
          }
          catch (LdapException ex)
          {
              MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
          }
          catch (Exception ex)
          {
              MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
          }
      }
      
      private void btnValidate2_Click(object sender, RoutedEventArgs e)
      {
          try
          {
              LdapConnection lcon = new LdapConnection(new LdapDirectoryIdentifier((string)null, false, false));
              NetworkCredential nc = new NetworkCredential(txtUsername.Text,
                                     txtPassword.Text, txtDomainName.Text);
              lcon.Credential = nc;
              lcon.AuthType = AuthType.Negotiate;
              lcon.Bind(nc);
      
              MessageBox.Show("Validation Success.");
          }
          catch (LdapException ex)
          {
              MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
          }
          catch (Exception ex)
          {
              MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
          }
      }
      

      【讨论】:

        【解决方案14】:

        这是我的完整身份验证解决方案供您参考。

        首先,添加以下四个引用

         using System.DirectoryServices;
         using System.DirectoryServices.Protocols;
         using System.DirectoryServices.AccountManagement;
         using System.Net; 
        
        private void AuthUser() { 
        
        
              try{
                    string Uid = "USER_NAME";
                    string Pass = "PASSWORD";
                    if (Uid == "")
                    {
                        MessageBox.Show("Username cannot be null");
                    }
                    else if (Pass == "")
                    {
                        MessageBox.Show("Password cannot be null");
                    }
                    else
                    {
                        LdapConnection connection = new LdapConnection("YOUR DOMAIN");
                        NetworkCredential credential = new NetworkCredential(Uid, Pass);
                        connection.Credential = credential;
                        connection.Bind();
        
                        // after authenticate Loading user details to data table
                        PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                        UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
                        DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
                        DirectorySearcher deSearch = new DirectorySearcher(up_User);
                        SearchResultCollection results = deSearch.FindAll();
                        ResultPropertyCollection rpc = results[0].Properties;
                        DataTable dt = new DataTable();
                        DataRow toInsert = dt.NewRow();
                        dt.Rows.InsertAt(toInsert, 0);
        
                        foreach (string rp in rpc.PropertyNames)
                        {
                            if (rpc[rp][0].ToString() != "System.Byte[]")
                            {
                                dt.Columns.Add(rp.ToString(), typeof(System.String));
        
                                foreach (DataRow row in dt.Rows)
                                {
                                    row[rp.ToString()] = rpc[rp][0].ToString();
                                }
        
                            }  
                        }
                     //You can load data to grid view and see for reference only
                         dataGridView1.DataSource = dt;
        
        
                    }
                } //Error Handling part
                catch (LdapException lexc)
                {
                    String error = lexc.ServerErrorMessage;
                    string pp = error.Substring(76, 4);
                    string ppp = pp.Trim();
        
                    if ("52e" == ppp)
                    {
                        MessageBox.Show("Invalid Username or password, contact ADA Team");
                    }
                    if ("775​" == ppp)
                    {
                        MessageBox.Show("User account locked, contact ADA Team");
                    }
                    if ("525​" == ppp)
                    {
                        MessageBox.Show("User not found, contact ADA Team");
                    }
                    if ("530" == ppp)
                    {
                        MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
                    }
                    if ("531" == ppp)
                    {
                        MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
                    }
                    if ("532" == ppp)
                    {
                        MessageBox.Show("Password expired, contact ADA Team");
                    }
                    if ("533​" == ppp)
                    {
                        MessageBox.Show("Account disabled, contact ADA Team");
                    }
                    if ("533​" == ppp)
                    {
                        MessageBox.Show("Account disabled, contact ADA Team");
                    }
        
        
        
                } //common error handling
                catch (Exception exc)
                {
                    MessageBox.Show("Invalid Username or password, contact ADA Team");
        
                }
        
                finally {
                    tbUID.Text = "";
                    tbPass.Text = "";
        
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多