System.DirectoryServices.DirectoryEntry是否包含实际使用Ldap的"domain\username"的构造函数?

Joh*_*win 8 c# directoryservices ldap active-directory

Microsoft有一篇通用知识库文章(Q316748),描述了如何使用该DirectoryEntry对象对Active Directory进行身份验证.在他们的示例中,他们通过将域名和用户名连接到标准NetBIOS格式("domain\username")并将其作为参数传递给目录条目构造函数来生成用户名值:

string domainAndUsername = domain + @"\" + username;
DirectoryEntry entry = new DirectoryEntry(_path, domainAndUsername, pwd);
Run Code Online (Sandbox Code Playgroud)

它最近引起了我们的注意,用户名的域名部分被完全忽略,在多个环境中我已经确认了这种行为.实际上正在使用用户名和密码,因为身份验证在无效时失败,但可以为域名和身份验证传递提供任意值.我一眼就认为这种格式适用于基于WinNT的目录访问,但LDAP的域部分被忽略.

谷歌检查显示许多LDAP示例将"域\用户名"值传递给DirectoryEntry对象,所以我要么在我的配置中弄乱了一些东西,或者很多人对KB文章感到困惑.任何人都可以确认这是预期的行为或建议一种方法来接受"域\用户名"值并与他们对Active Directory进行身份验证?

谢谢,

Per*_*alt 18

简答:当构造函数的path参数DirectoryEntry包含无效域名时,DirectoryEntry对象将(在forrest中搜索无效域失败后)尝试通过删除username参数的域部分并使用纯用户名尝试连接来回退(sAMAccountName赋).

答案很长:如果username参数中指定的域名无效,但用户存在于path参数中指定的域中,则将对用户进行身份验证(通过使用回退).但是,如果用户存在于forrest中的另一个域中,那么path参数身份验证中指定的用户只有在包含参数的域部分username并且正确时才会成功.

在处理DirectoryEntry-objects时,有四种不同的方法来指定username参数:

  • 专有名称(CN =用户名,CN =用户,DC =域,DC =本地)
  • NT帐户名称(DOMAIN \用户名)
  • 普通帐户名/ sAMAccountname(用户名)
  • 用户主体名称(通常为username@domain.local)

让我用一个例子来说明:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.DirectoryServices;

namespace DirectoryTest
{
  class Program
  {

    private static Int32 counter = 1;

    static void Main(string[] args)
    {
      TestConnection();
    }

    private static void TestConnection()
    {
      String domainOne = "LDAP://DC=domain,DC=one";
      String domainOneName = "DOMAINONE";
      String domainOneUser = "onetest";
      String domainOnePass = "testingONE!";

      String domainTwo = "LDAP://DC=domain,DC=two";
      String domainTwoName = "DOMAINTWO";
      String domainTwoUser = "twotest";
      String domainTwoPass = "testingTWO!";

      String invalidDomain = "INVALIDDOMAIN";

      // 1) This works because it's the correct NT Account Name in the same domain:
      Connect(domainOne, domainOneName + "\\" + domainOneUser, domainOnePass);

      // 2) This works because username can be supplied without the domain part
      // (plain username = sAMAccountName):
      Connect(domainOne, domainOneUser, domainOnePass);

      // 3) This works because there's a fall back in DirectoryEntry to drop the domain part
      // and attempt connection using the plain username (sAMAccountName) in (in this case)
      // the forrest root domain:
      Connect(domainOne, invalidDomain + "\\" + domainOneUser, domainOnePass);

      // 4) This works because the forrest is searched for a domain matching domainTwoName:
      Connect(domainOne, domainTwoName + "\\" + domainTwoUser, domainTwoPass);

      // 5) This fails because domainTwoUser is not in the forrest root (domainOne)
      // and because no domain was specified other domains are not searched:
      Connect(domainOne, domainTwoUser, domainTwoPass);

      // 6) This fails as well because the fallback of dropping the domain name and using
      // the plain username fails (there's no domainTwoUser in domainOne):
      Connect(domainOne, invalidDomain + "\\" + domainTwoUser, domainTwoPass);

      // 7) This fails because there's no domainTwoUser in domainOneName:
      Connect(domainOne, domainOneName + "\\" + domainTwoUser, domainTwoPass);

      // 8) This works because there's a domainTwoUser in domainTwoName:
      Connect(domainTwo, domainTwoName + "\\" + domainTwoUser, domainTwoPass);

      // 9) This works because of the fallback to using plain username when connecting
      // to domainTwo with an invalid domain name but using domainTwoUser/Pass:
      Connect(domainTwo, invalidDomain + "\\" + domainTwoUser, domainTwoPass);
    }

    private static void Connect(String path, String username, String password)
    {
      Console.WriteLine(
        "{0}) Path: {1} User: {2} Pass: {3}",
        counter, path, username, password);
      DirectoryEntry de = new DirectoryEntry(path, username, password);
      try
      {
        de.RefreshCache();
        Console.WriteLine("{0} = {1}", username, "Autenticated");
      }
      catch (Exception ex)
      {
        Console.WriteLine("{0} ({1})", ex.Message, username);
      }
      Console.WriteLine();
      counter++;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,domain.one是forrest根域,domain.two与domain.one在同一个forrest中(但自然是一个不同的树).

因此,要回答您的问题:如果用户不在我们要连接的域中且在username参数中未指定或者无效的域名,则身份验证将始终失败.