C# 调用LDAP 验证账户

首先去下载两个NuGet程序包

using System.DirectoryServices.AccountManagement;

using System.DirectoryServices.ActiveDirectory;

后边都会用到。

另外可以参考官方文档,再有不明白的就拿关键词去度一度吧

System.DirectoryServices.ActiveDirectory 命名空间 | Microsoft Learn

 

/// <summary>
        ///验证账户名密码是否正确
        /// </summary>
        /// <param name="userid"></param>
        /// <param name="pwd"></param>
        /// <returns></returns> public bool VerifyUserPwd(string userid, string pwd)
        {
            try
            {
                string Domain = "";
                Domain = System.Configuration.ConfigurationManager.AppSettings["Domain"].ToString();
                if (!string.IsNullOrEmpty(userid) || !string.IsNullOrEmpty(pwd))
                {
                    PrincipalContext adcontext = new PrincipalContext(ContextType.Domain, Domain, userid, pwd);
                    using (adcontext)
                    {
                        if (adcontext.ValidateCredentials(userid, pwd))
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }

                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                return false;
            }

        }

 

 /// <summary>
        /// 验证该用户是否在该工作组
        /// </summary>
        /// <param name="ADGroup">工作组List,我可以传不止一个组</param>
        /// <param name="Domain">域名</param>
        /// <param name="userid">用户名</param>
        /// <returns></returns>
        public bool checkADGroup(string ADGroup, string Domain, string userid)
        {
            bool isMember = false;
            //Domain = "corp.****corp.net";
            if (ADGroup == "NA")//ADGroup和Domain 在appsetting中配置
            {
                return true;
            }

            List<String> ADGroupList = new List<string>(ADGroup.Split(';'));
            foreach (string everyADGroup in ADGroupList)
            {
                bool result = false;
                if (!String.IsNullOrEmpty(everyADGroup))
                {
                    string container = Domain.Replace(".", ",DC=");
                    PrincipalContext domainctx = new PrincipalContext(ContextType.Domain, Domain.Split('.')[0], "DC=" + container);
                    UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(domainctx, IdentityType.SamAccountName, userid);
                    result = userPrincipal.IsMemberOf(domainctx, IdentityType.Name, everyADGroup);
                    
                    if (result == true)
                    {
                        isMember = result;//只有得到正确验证之后才赋值为true
                    }
                }
            }

            return isMember;
        }

 

///这个方法可以打断点看返回结果,返回值比较全。看需求去取。
public void DomainModeCheck(string UserName, string Password, string Domain)
        {
            if (Domain=="") 
            Domain = System.Configuration.ConfigurationManager.AppSettings["Domain"].ToString();
            if (!string.IsNullOrEmpty(UserName) || !string.IsNullOrEmpty(Password))
            {
                return ;
            }
            DirectoryContext oContext = null;

            oContext = new DirectoryContext(DirectoryContextType.Domain, Domain, UserName, Password);
            var result = System.DirectoryServices.ActiveDirectory.Domain.GetDomain(oContext);
            DomainMode DM = System.DirectoryServices.ActiveDirectory.Domain.GetDomain(oContext).DomainMode;


        }

 

使用的时候如果看到像ContextType contextType,ContextOptions options这样的参数,一开始我也不太理解,后来经过我的组长解释。这种参数是枚举型,我的理解是,

拿ContextType contextType具体,点击F12进去看定义(如下图1),就是说Context中有三种参数,如果在实例化public PrincipalContext(ContextType contextType, string name, string userName, string password);的时候指定了ContextType.Domain,那么在name处的参数就要传Domain,如果指定了ContextType.Machine,那么在name处就应该传Machine。就是说,前面指定名字,后边要指定名字对应的值才可以(如下图2)。

 

 图1

 

 

图2

 

 

    • CN =通用名称
    • OU =组织单位
    • DC =域组件

 

    • CN = Common Name
    • OU = Organizational Unit
    • DC = Domain Component

 

差不多就这些,以后遇到再补充

 

2023-08-17

通用的/获取用户组目录对象方法:

  1         /// <summary>
  2         /// 根据用户名密码建立活动目录连接
  3         /// </summary>
  4         /// <returns>目录对象或空</returns>
  5         private static DirectoryEntry GetDirectoryObject()
  6         {
  7             DirectoryEntry result;
  8             //查询目录对象
  9             DirectoryEntry entry = new DirectoryEntry(ADPath, ADUser, ADPassword, AuthenticationTypes.Secure);
 10            
 11             if (entry != null)
 12             {
 13                 //活动目录连接成功
 14                 result = entry;
 15             }
 16             else
 17             {
 18                 //活动目录连接失败
 19                 result = null;
 20             }
 21             return result;
 22         }
 23 
 24 
 25         /// <summary>
 26         /// 根据目录对象名和类型查询目录对象
 27         /// </summary>
 28         /// <param name="pName">目录对象名</param>
 29         /// <param name="pType">目录对象类型 user/group</param>
 30         /// <returns>目录对象或空</returns>
 31         public static DirectoryEntry GetDirectoryEntryByType(string pName, string pType)
 32         {
 33             //初始化返回值
 34             DirectoryEntry retResult;
 35             //初始化查询字符串
 36             string strFilter = string.Empty;
 37             switch (pType)
 38             {
 39                 //查询用户
 40                 case "user":
 41                     strFilter = "(&(&(objectCategory=person)(objectClass=user))(sAMAccountName=" + pName + "))";
 42                     break;
 43                 //查询用户组
 44                 case "group":
 45                     strFilter = "(&(objectClass=group)(sAMAccountName=" + pName + "))";
 46                     break;
 47                 //查询计算机
 48                 case "computer":
 49                     strFilter = "(&(objectClass=computer)(name=" + pName + "))";
 50                     break;
 51                 //查询人员
 52                 case "person":
 53                     strFilter = "(&(objectCategory=person)(sAMAccountName=" + pName + "))";
 54                     break;
 55                 //查询联系人
 56                 case "contact":
 57                     strFilter = "(&(&(objectCategory=person)(objectClass=contact))(sAMAccountName=" + pName + "))";
 58                     break;
 59                 //查询组织单位
 60                 case "organizationalUnit":
 61                     strFilter = "(&(objectCategory=organizationalUnit)(name=" + pName + "))";
 62                     break;
 63                 //默认不指定类型,进行查询
 64                 default:
 65                     strFilter = "(name=" + pName + ")";
 66                     break;
 67             }
 68 
 69             //开始连接活动目录
 70             DirectoryEntry de = GetDirectoryObject();
 71             if (de != null)
 72             {
 73                 DirectorySearcher deSearch = new DirectorySearcher(de);
 74                 deSearch.Filter = strFilter;
 75                 deSearch.SearchScope = SearchScope.Subtree;
 76 
 77                 //根据查询字符串搜索目录对象
 78 
 79                 try
 80                 {
 81                     SearchResult result = deSearch.FindOne();
 82                     de = new DirectoryEntry(result.Path);
 83 
 84                     //查询到目录对象
 85                     retResult = de;
 86                 }
 87                 catch (Exception ex)
 88                 {
 89                     //异常错误
 90                     //未查询到目录对象
 91                     retResult = null;
 92                 }
 93             }
 94             else
 95             {
 96                 //连接目录失败
 97                 retResult = null;
 98             }
 99             return retResult;
100         }
View Code

 

通过用户组目录获取组内成员:

 1 //上个方法中返回的DirectoryEntry实体deGroup
 2 List<string> memberList = new List<string>();
 3 if (deGroup != null)
 4                 {
 5                     //开始查询用户组成员
 6                     if (deGroup.Properties["member"] != null)
 7                     {
 8                         for (int i = 0; i < deGroup.Properties["member"].Count; i++)
 9                             //for (int i = 0; i < 10; i++)
10                             {
11                             string pathMember = deGroup.Properties["member"][i].ToString();
12                             DirectoryEntry deMember = ADHelper.GetDirectoryEntryByString(pathMember);
13 
14                             string initial = deMember.Properties["sAMAccountName"][0].ToString();
15                             memberList.Add(initial);
16                         }
17                 }
18                     else
19                     {
20                     }
21                 }
22                 else
23                 {
24                     //用户组不存在
25                     LogID = "1504";
26                     strMsg=QueueLogger.Info(LogID, messageLanguage,pGroup);
27                 }
View Code

 

1 public static DirectoryEntry GetDirectoryEntryByString(string pString)
2         {
3             DirectoryEntry de = new DirectoryEntry(ADPrefixion + pString, ADUser, ADPassword, AuthenticationTypes.Secure);
4             return de;
5         }
GetDirectoryEntryByString

 

在获取组员的过程中发现最多获取member 1500个,若成员超过1500个,则怎么都获取不到后面的。

在stackoverflow上搜索到的解决方法是c# - get SamAccountName from large AD groups with DirectorySearcher - Stack Overflow

但应用的时候不生效,不管member;range=0-200还是member;range=0-1499,最后得到都是那前1500个成员。

这时候必须怀疑到LDAP域控服务器的设置,利用AD域控制器 修改查询记录最大值1000的限制_伊一不舍的博客-CSDN博客 里面的方法查看服务器属性。

还有个官方文档:使用 Ntdsutil 查看和设置轻型目录访问协议 (LDAP) 策略 - Windows Server | Microsoft Learn

可以看到属性中有个MaxValRange,这个默认是1500,这也是我参考了官网LDAP policies | Microsoft Learn。(但也要确认一下服务器的版本哦)

 

关于MaxValRange的一些解释和修改方式:

DC 在 LDAP 响应中仅返回 5000 个值 - Windows Server | Microsoft Learn

[MS-ADTS]: LDAP Policies | Microsoft Learn

MaxValRange 是非默认配置 | Microsoft Learn

基本上看完这些链接就能明白了,

posted on 2022-09-23 11:06  张不胖  阅读(488)  评论(0编辑  收藏  举报

导航