使用服务帐户进行活动目录身份验证

本文关键字:活动 身份验证 服务 | 更新日期: 2023-09-27 18:34:55

我正在研究Web Services,它可以针对Active Directory对用户进行身份验证。我目前的解决方案正在起作用,但是,我正在尝试采取不同的方法。

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

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

例如,我的生产活动目录(比如说域域.local(和我的DMZ 活动目录(比如说域域域.公共(。在对任何 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
}
}

如果 AD 中存在用户名DMZ它将返回 true,否则它将返回 false。但是,将有一些用户仅存在于生产 AD 中,但在 DMZ 中没有任何条目。既然,我建立了单向信任,我应该能够做到这一点:

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

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

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

谢谢

使用服务帐户进行活动目录身份验证

域在 PrincipalContext 中的值是什么? 如果尚未使用,则需要为要查找的用户使用正确的域,以使用当前代码。 主体上下文不支持跨林中的域搜索。

下面是一个使用 DirectorySearcher 来执行此操作的示例。

代码有几个问题,我只谈论主要问题。

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

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

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

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

  3. isActive,"66050"从何而来?
    66060 是十六进制中的 10202,表示 (1( 用户、(2( 禁用和 (3( 密码不会过期。

    要检查帐户是否已启用,请仅选中帐户禁用位 (0x0002(。它应为 0 表示已启用。

  4. 您已经在 DoesUserExist 中获取了用户主体。可以通过查看UserPrincipal.Enabled来简单地检查启用。

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