【问题标题】:Active Directory Authentication using services account使用服务帐户的 Active Directory 身份验证
【发布时间】:2015-02-20 20:53:41
【问题描述】:

我正在研究Web Services,它根据Active Directory 对用户进行身份验证。我目前的解决方案是有效的,但是,我正在尝试采用不同的方法。

我有一个位于防火墙后面的Active Directory(生产)。我还在DMZ 中安装了Active Directory。它们之间存在一种单向关系。 DMZ 信任生产,而生产并不关心DMZ

我想要完成的是通过DMZ Active Directory 对每个人进行身份验证。目前,根据用户名,我知道要针对哪个 AD 服务器进行身份验证。

例如,我的生产 Active Directory(比如说域 domain.local)和我的 DMZ Active Directory(比如说域 domain.public)。在对任何 AD 服务器进行身份验证之前,我会检查提供的用户名是否存在于其中一台服务器中。然后,我检查用户是否处于活动状态,然后才进行身份验证。 (我在第一个函数中遇到问题。它永远不会到达第二个或第三个函数)。

更新:添加了所有内容:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.DirectoryServices;
using System.Security.Principal;
using System.DirectoryServices.AccountManagement;

namespace ActiveDirectory
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
public class Service1 : IService1
{
    #region Does User Exist in AD

    public string local = string.Empty;
    public string ldappath = string.Empty;
    public string userNameToUse = string.Empty;
    public string domain = string.Empty;

    public bool DoesUserExist(string userName)
    {
        string _userName = userName;

        bool exist = true;

        using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
        {
            using (var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userNameToUse))
            {
                if (foundUser == null)
                {
                    exist = false;
                }
                else
                {
                    return exist;
                }
            }
        }
        return exist;
    }

    #endregion

    #region Check if User Active
    public bool isActive (string userName)
    {
        string _userNameToBeSearched = userNameToUse;
        string _username = string.Empty;
        string _pwd = string.Empty;

        if (local == "YES")
        {
             _username = "xx";
             _pwd = "xx";
            ldappath = "LDAP://xxx/DC=xx, DC=local";
        }
        else
        {
             _username = "xx";
             _pwd = "xx";
             ldappath = "LDAP://xxx/DC=xx, DC=public";
        }

        bool isActive = true;

        try
        {
            DirectoryEntry entry = new DirectoryEntry(ldappath, _username, _pwd);
            DirectorySearcher search = new DirectorySearcher(entry);
            entry.AuthenticationType = AuthenticationTypes.Secure;
            search.SearchRoot = entry;
            search.Filter = "(SAMAccountName=" + _userNameToBeSearched + ")";

            SearchResult results = search.FindOne();

            if (results.ToString() != "")
            {
                int flags = Convert.ToInt32(results.Properties["userAccountControl"][0].ToString());

                //CHECK IF THE ACCOUNT IS DISABLED
                if (flags == 66050)
                {
                    isActive = false;
                }
            }
        }
        catch (DirectoryServicesCOMException ex)
        {
            ex.ToString();
        }
        return isActive;
    }

    #endregion

    #region Is user authenticated
    public string isAuthenticated (string userName, string pwd)
    {
        string _userName, _pwd, message;
        _userName = userName;
        _pwd = pwd;

        char[] splitchar = { '@' };

        string[] strSplit = _userName.Split(splitchar);

        string z = strSplit[0];

        if (strSplit.Length == 2)
        {
            domain = "x.public";
            userNameToUse = z.ToString();
            local = "NO";
        }
        else
        {
            domain = "x.local";
            userNameToUse = z.ToString();
            local = "YES";
        }

        if (DoesUserExist (userNameToUse) == true)
        {
            if (isActive(userNameToUse) == true)
            {
                try
                {
                    DirectoryEntry entry = new DirectoryEntry(ldappath, userNameToUse, _pwd);
                    object nativeObject = entry.NativeObject;
                    var GUIDID = "";

                    using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
                    {
                        using (var user = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userNameToUse))
                        {
                            if (user != null)
                            {
                                GUIDID = user.Guid.ToString();
                            }
                        }
                        message = "Successfully authenticated:" + GUIDID;
                    }
                }
                catch (DirectoryServicesCOMException)
                {
                    message = "Invalid password.";
                }
            }
            else
            {
                message = "Account is disabled";
            }
        }
        else
        {
            message = "There's an issue with your account.";
        }
        return message;      
    }

    #endregion
}
}

如果DMZ AD 中存在用户名,则返回 true,否则返回 false。但是,将有一些用户只存在于生产 AD 中,但在 DMZ 中没有任何条目。因为,我建立了一种方式,相信我应该能够做到这一点:

username@domain.local 用于生产,username@domain.public 但是,即使我指定了完整的用户名,如果 DMZ AD 中不存在条目,它也会返回 null,尽管它存在于生产 AD 中。

任何建议,关于我如何通过DMZ AD 使用对生产广告具有完全权限的 web 服务帐户对每个人进行身份验证?

注意如果需要我可以提供其余的代码...*

谢谢

【问题讨论】:

  • 上面的程序是用哪个用户来运行的?您必须使用生产 AD 的凭据才能访问这两个 AD。 userName的格式和domain的内容是什么?
  • 是每个用户。我在两个 AD 中都有服务帐户。典型的用户名是 sjohns,将针对生产环境进行验证,sjohns@mail.com 将针对 DMZ 进行验证。
  • 如果@codingChris 的回答不符合您的需要,请提供其余代码。上述方法只取1个参数userName。我仍然不太明白domainuserNameToUse 的价值从何而来......
  • @baldpate,感谢您的回复。我添加了所有代码。从应用程序调用函数isAuthenticated。如果您有任何问题,请告诉我。

标签: c# web-services active-directory


【解决方案1】:

PrincipalContext 中域的值是什么?如果您还没有,您需要为您正在查找的用户使用正确的域来使用您当前的代码。 PrincipalContext 不支持在您的林中跨域进行搜索。

这是使用 DirectorySearcher 执行此操作的 example

【讨论】:

  • 感谢您的回复。我添加了整个代码。这个函数isAuthenticated首先被调用。
  • 编辑速度不够快... 1. 你有测试吗?如果您可以单步执行此代码,您可能可以准确指出问题出在哪里以及发生了什么。 2. 我认为在我们通过用户名验证后,您需要在DoesUserExist 方法中使用DirectorySearcher。我认为 PrincipalContext 无法进入本地域。不幸的是,我没有像你这样的设置来测试。
  • 是的,我确实有测试结果。发生的第一件事是DoesUserExist。在这里我检查这个用户名是否存在。我正在检查 DMZ 域,但是,我正在传递用户名 jsmith@domain.local。以下行 var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userNameToUse) 返回 null。该用户在 DMZ 中不存在,但在生产中确实存在。那么通过限定域不应该返回用户吗?
  • 即使您知道应该在哪个域中搜索,PrincipalContext 也不支持从 .public dmz 服务器搜索 .local 域中的用户。您需要在该方法中设置 DirectorySearcher,而不是使用 S.DS.AM 和 FindByIdentity。
  • 我倾向于DirectorySearcher1,但是,当我正在编写一些代码时,我发现我无法在多个域中进行搜索。我有两个域,并且用户都位于这两个域中,但是在某些情况下,用户只存在于一个域中,而没有存在于另一个域中。关于如何在多个域中搜索的任何建议?谢谢...
【解决方案2】:

代码有几个问题,我只讲主要的。

  1. 在方法DoesUserExist 中,您使用服务帐户(没有凭据传递给PrincipalContext)来查询两个域。但是在方法isActive 中,您对不同的域有不同的凭据。所以我们有 2 个域的 3 个凭据...

    至少对这两种方法使用一致的方式。

  2. 对于单向信任(例如 A 信任 B),B 中的帐户应该能够访问域 A 和域 B。

    也许您可以简单地使用受信任域中的帐户作为服务帐户。然后使用服务帐户(用户名和密码为空)执行所有 AD 访问。所以你甚至不需要在你的代码中输入密码。

  3. isActive中的“66050”从何而来?
    66060 是十六进制的 10202,这意味着(1)用户,(2)禁用和(3)密码不会过期。

    要检查帐户是否已启用,请仅检查 ACCOUNTDISABLE 位 (0x0002)。启用时应为 0。

  4. 您已经在 DoesUserExist 中获得了 UserPrincipal。可以通过查看UserPrincipal.Enabled 来简单地检查是否启用。

我需要在日常工作中进行代码审查。现在我也在 Stack Overflow 上这样做...... :)

【讨论】:

  • 感谢您的回复。我刚开始编写这段代码,它很可能会不断变化。我的主要目标是为提供的usernamepassword 执行Authentication。比如A (dmz)信任B (production),用户jsmith只存在于B,但需要使用A进行认证。我应该采取什么方法?谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-02-14
  • 2011-01-22
  • 2010-11-05
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 2015-08-23
  • 1970-01-01
相关资源
最近更新 更多