当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《验证ActiveDirectory的用户名和密码?》经验,为你挑选了10个好方法。

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



1> marc_s..:

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

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

管理.NET Framework 3.5中的目录安全性主体

System.DirectoryServices.AccountManagement上的MSDN文档

更新:

正如其他SO问题(及其答案)中所述,此调用可能会返回True用户的旧密码.只要注意这种行为,如果发生这种情况就不要太惊讶:-)(感谢@MikeGledhill指出这一点!)


还要注意"访客"帐户 - 如果启用了域级访客帐户,如果您为其提供*不存在*用户,则ValidateCredentials将返回true.因此,您可能希望调用"UserPrinciple.FindByIdentity"以查看传入的用户ID是否首先存在.
在我的域中,我必须指定pc.ValidateCredentials("myuser","mypassword",ContextOptions.Negotiate)或者我会得到System.DirectoryServices.Protocols.DirectoryOperationException:服务器无法处理目录请求.
如果密码已过期或帐户已禁用,则ValidateCredentials将返回false.不幸的是,它没有告诉你*为什么*它返回false(这很可惜,因为这意味着我不能做一些明智的事情,比如重定向用户更改密码).
@AlexPeck:你必须这样做(像我一样)的原因是.NET默认使用以下技术:LDAP + SSL,Kerberos,然后是RPC.我怀疑你的网络中的RPC是关闭的(好!)并且除非你使用`ContextOptions.Negotiate`明确告诉它,否则Kerberos实际上并没有被.NET使用.
你确定吗???我尝试这个时得到了PrincipalServerdownException ...
请注意,如果用户更改了他们的Active Directory密码,这段代码将继续使用旧的AD密码认真地对用户进行身份验证.是的,真的.请在此处阅读:http://stackoverflow.com/questions/8949501/why-does-active-directory-validate-last-password
我还想补充一点,我需要设置`ContextOptions.Negotiate`来修复生产服务器上的性能问题.验证凭据需要20-30秒而不指定枚举.
注意 - 如果你使用它来验证并且你有一个AD锁定策略(例如,三次不正确的尝试),它会锁定你.
如果你得到DirectoryOperationException,而不是像上面的评论中提到的那样直接下降到Negotiate,那么至少试试Negotiate | 签名| 首先密封.您可能会认为这是默认情况下没有任何选项(基于PrincipalContext构造函数中的文档),但如果您使用ILSpy,您将看到它尝试的默认值实际上是简单的| SecureSocketLayer.有了这种类型的异常,它就会失败并且不会尝试其他任何东西.试试谈判| 签名| 密封,如果有效,你仍然是安全的.这就是我发生的事情.

2> DiningPhilan..:

我们在内联网上这样做

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


永远不需要在`using`变量上显式调用`Close()`.
你在"路径"中放了什么?域名?服务器的名称?域的LDAP路径?服务器的LDAP路径?
答1:不,我们将其作为Web服务运行,因此可以从主Web应用程序中的多个位置调用它.答案2:路径包含LDAP信息... LDAP:// DC = domainname1,DC = domainname2,DC = com
这似乎可以允许LDAP注入.您可能希望确保转义或删除strAccountId中的任何括号
此代码是否不需要以AD用户身份运行?

3> Søren Mors..:

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

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)


不要忘记添加一些对项目的引用:`System.DirectoryServices`和`System.DirectoryServices.Protocols`
但是,我的问题是:如何获取LDAP服务器名称?如果要编写可移植的应用程序,则不能指望用户知道或需要在每个网络上提供AD服务器的名称。
不幸的是,某些AD安装程序未返回LDAP子错误代码,这意味着该解决方案将无法工作。

4> Steven A. Lo..:

使用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> Alan..:

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

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

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

用户密码已过期.

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

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

ERROR_PASSWORD_MUST_CHANGE = 1907

ERROR_PASSWORD_EXPIRED = 1330



6> stephbu..:

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

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

MSDN参考这里......

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

绝对要使用登录类型

LOGON32_LOGON_NETWORK (3)

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


@cciotti:不,那是错的.正确验证某人的最佳方法是使用LogonUserAPI作为@stephbu写入.本文中描述的所有其他方法都不会100%工作.但请注意,我确实认为您必须加入域才能调用LogonUser.
“ LogonUser” API要求用户具有“作为操作系统一部分的特权”特权;这不是用户获得的东西,也不是您想要授予组织中每个用户的东西。(http://msdn.microsoft.com/zh-cn/library/aa378184(v=vs.85).aspx)

7> Mathieu Gars..:

完整的.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服务器上的信息!


如果在检查凭据之前由于任何其他原因引发了COMException,这是否不允许任何人通过?

8> palswim..:

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



9> Charles Bret..:

试试这段代码(注意:报告不能在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"创建自己的自定义异常



10> 小智..:

如果你坚持使用.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;
}   

推荐阅读
李桂平2402851397
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有