使用 DirectoryServices 进行跨域身份验证

发布于 2025-01-07 03:20:12 字数 1200 浏览 0 评论 0原文

我需要为我的 Intranet Web 应用程序创建一种方法,该方法将使用 DirectoryServices 针对默认域或用户指定的域对用户进行身份验证。

在我的登录表单上,用户可以以“用户名”和“密码”或“域\用户名”的形式提供凭据代码>和<代码>“密码” 当用户与网络服务器位于同一域时可以使用第一种情况,并且非常简单。我使用的代码是:

 string domain = "";
 // Code to check if the username is in form of "domain\user" or "user"
 string username = ParseUsername(username, out domain);
 if(domain == "")
    domain = defaultDomain;

 PrincipalContext context = new PrincipalContext(ContextType.Domain, domain, username, password); 
 bool IsAuthenticated = context.ValidateCredentials(username, password)

我将用户名和密码传递给PrincipalContext构造函数,以便在我尝试访问另一个域的情况下绑定调用。

对于本地域,代码工作正常。但是,当我尝试检查通过用户名指定的另一个域时,我收到“无法联系服务器”错误。

我还尝试使用不同的 ContextOptions,例如 ContextOptions.SimpleBind 或 ContextOptions.Negotiate,但我似乎总是得到相同的结果。

我需要实现这一点,因为应用程序正在运送给具有单域或多域环境的各种客户。 在“远程”域的情况下我还应该指定其他内容吗?代码需要灵活,因为它将部署在各种环境中。

谢谢

编辑:我必须指出,我更喜欢使用 DirectoryServices.AccountManagementPrincipalContext 来执行此操作,以便利用它的其他功能也提供。

另外,我必须提到,对于我的测试,我的开发计算机位于 10.0.0.* 网络上,而我测试的第二个域位于 10.0.1.* 上。我有一个路由等等,并且我可以使用 ldap 客户端成功连接,所以问题是为什么我无法通过我的 asp.net 应用程序连接到域。

I need to create a method for my intranet web application that will authenticate a user using DirectoryServices, either against a default domain, or a user specified one.

On my login form the user will be able to either give there credentials in the form of "username" and "password" or "domain\username" and "password"
The first case can be used when the user is in the same domain as the webserver and is quite straightfoward. The code I use is:

 string domain = "";
 // Code to check if the username is in form of "domain\user" or "user"
 string username = ParseUsername(username, out domain);
 if(domain == "")
    domain = defaultDomain;

 PrincipalContext context = new PrincipalContext(ContextType.Domain, domain, username, password); 
 bool IsAuthenticated = context.ValidateCredentials(username, password)

I pass the username and password to the PrincipalContext constructor in order to bind the call in cases where I try to access another domain.

For the local domain the code works fine. However when I try to check against another domain that is being specified through the username, then I get a "Server could not be contacted" error.

I also tried using different ContextOptions such as ContextOptions.SimpleBind or ContextOptions.Negotiatebut I always seem to be getting the same result.

I need to implement this, since the application is being shipped to various customers, with either single domain or multiple domain environments.
Is there something else I should specify in cases of "remote" domains? The code needs to be flexible since this will be deployed in various environments.

Thanks

EDIT: I must point out, that I prefer to do it using DirectoryServices.AccountManagement and PrincipalContext in order to take advantage of other functionality it provides as well.

Also, I must mention that for my tests, my Dev machine is on a 10.0.0.* network and the second domain I test against is on a 10.0.1.*. I have a route and all, and I can succesfuly connect using an ldap client, so the question is why I cannot connect to the domain via my asp.net application.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

秋叶绚丽 2025-01-14 03:20:12

我已经想出了这个问题的解决方案。

为了支持多个域(无论是在信任关系中还是在隔离网络中),首先我在 web.config 中添加了一个 NameValueCollection 来列出域及其域控制器。

  <domains>
    <add key="domain1" value="10.0.0.1"/>
    <add key="domain2" value="10.0.1.11"/>
  </domains>

(有关配置添加的更多信息,请参见 这个问题

然后下一步是按照我在问题中提到的方式从用户的凭据中读取域。获取域后,我尝试从配置值中查找相应的域控制器,以获得正确的 LDAP 连接字符串。所以我的方法是这样的:

private string GetLDAPConnection(string a_Domain, string a_Username, string a_Password)
{
    // Get the domain controller server for the specified domain
    NameValueCollection domains = (NameValueCollection)ConfigurationManager.GetSection("domains");
    string domainController = domains[a_Domain.ToLower()];

    string ldapConn = string.Format("LDAP://{0}/rootDSE", domainController);

    DirectoryEntry root = new DirectoryEntry(ldapConn, a_Username, a_Password);
    string serverName = root.Properties["defaultNamingContext"].Value.ToString();
    return string.Format("LDAP://{0}/{1}", domainController, serverName);
}

一旦我返回正确的连接字符串,我就会进行一个新的调用,通过寻址正确的 LDAP 来验证用户身份

    ...
    string ldapConn = GetLDAPConnection(domain, username, a_Password);                             
    DirectoryEntry entry = new DirectoryEntry(ldapConn, username, a_Password);        

    try
    {
        try
        {
            object obj = entry.NativeObject;
        }
        catch(DirectoryServicesCOMException comExc)
        {
            LogException(comExc);
            return false;
        }

        DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = string.Format("(SAMAccountName={0})", username);
        search.PropertiesToLoad.Add("cn");
        SearchResult result = search.FindOne();

从这一点开始,我还可以执行我想要的所有其他查询,例如用户的组 。

由于对远程域的调用需要绑定到用户,因此我使用“调用”用户凭据 这样,用户就可以通过身份验证,并且呼叫将绑定到特定用户。此外,我还指定了一个“默认”域,以应对用户提供凭据而不指定域的情况。

然而,我没有按照我想要的方式使用PrincipalContext,但好的一面是,该解决方案也适用于较旧的.NET 2.0 应用程序。

我不确定这是否是解决该问题的最佳解决方案,但它似乎在我们迄今为止执行的测试中有效。

I have come up with this solution to the problem.

In order to support multiple domains, either in trust relationships or even in isolated networks, first of all I added a NameValueCollection in my web.config to list the domains and their domain controllers.

  <domains>
    <add key="domain1" value="10.0.0.1"/>
    <add key="domain2" value="10.0.1.11"/>
  </domains>

(more info on the configuration addition in this so question)

Then the next step was to read the domain from the User's credentials in the way I mention in the question. Having gotten the domain I try to lookup the according domain controller from the configuration values, in order to get the proper LDAP connection string. So my method is this:

private string GetLDAPConnection(string a_Domain, string a_Username, string a_Password)
{
    // Get the domain controller server for the specified domain
    NameValueCollection domains = (NameValueCollection)ConfigurationManager.GetSection("domains");
    string domainController = domains[a_Domain.ToLower()];

    string ldapConn = string.Format("LDAP://{0}/rootDSE", domainController);

    DirectoryEntry root = new DirectoryEntry(ldapConn, a_Username, a_Password);
    string serverName = root.Properties["defaultNamingContext"].Value.ToString();
    return string.Format("LDAP://{0}/{1}", domainController, serverName);
}

Once I get back the proper connection string I make a new call in order to authenticate the user, by addressing the proper LDAP

    ...
    string ldapConn = GetLDAPConnection(domain, username, a_Password);                             
    DirectoryEntry entry = new DirectoryEntry(ldapConn, username, a_Password);        

    try
    {
        try
        {
            object obj = entry.NativeObject;
        }
        catch(DirectoryServicesCOMException comExc)
        {
            LogException(comExc);
            return false;
        }

        DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = string.Format("(SAMAccountName={0})", username);
        search.PropertiesToLoad.Add("cn");
        SearchResult result = search.FindOne();

From this point on I can also perform all the other queries I want such as the user's group membership etc.

Since the call to the remote domains needs to be bound to a user, I use the "calling" users credentials. This way the user get's authenticated and the Call is bound to the specific user. Furthermore, I specify a "default" domain, for cases where users provide their credentials without specifying the domain.

I did not manage to this however using the PrincipalContext as I wanted, but on the bright side, this solution is also applicable for older .NET 2.0 applications as well.

I am not sure that this is the best solution to the problem, however it seems to work in the tests we have so far performed.

月棠 2025-01-14 03:20:12

我不知道为什么我被否决了,但我认为这可能是错误的,您托管代码的服务器/域与您尝试联系的域之间的信任级别可能尚未建立。我无法向您提供可能发生这种情况的原因。

[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]

您可以尝试将其添加到您的函数上方,看看它是否可以帮助您完成,但除此之外,我不明白为什么在 WinNT 域上搜索所有可能的用户会是错误的。希望这有帮助

I don't know why I got downvoted but here's what I think might be wrong, the trust level between the server/domain on which you code is hosted and the domain to which you are trying to contact might not be established. I can't provide you reasons why that might be happening.

[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]

You can try adding this above your function and see if it helps you to go through, but apart from that I don't see why would it be wrong to search on WinNT domain for all the possible users. Hope this helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文