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 NameOU
= Organizational UnitDC
= 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 }
通过用户组目录获取组内成员:
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 }
1 public static DirectoryEntry GetDirectoryEntryByString(string pString) 2 { 3 DirectoryEntry de = new DirectoryEntry(ADPrefixion + pString, ADUser, ADPassword, AuthenticationTypes.Secure); 4 return de; 5 }
在获取组员的过程中发现最多获取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
基本上看完这些链接就能明白了,