最近由于项目需要研究了一下LDAP相关知识,感觉对没接触过的人来说还是有点坑的,所以记录下来给大家分享。
由于是第一次接触,就在网上搜了一些相关的文章,照着示例代码测试,却怎么也连不上LDAP服务器,最后折腾的能连上服务器了,又不能检索用户。
折腾过程中遇到的主要错误就是:
-
There is no such object on the server.
-
The username or password is incorrect.
-
The server could not be contacted.
在经历了N小时的煎熬之后,终于找到了第一种解决方案,其实就是参考网上的示例代码,但是示例代码的AuthenticationTypes是None,测试连接的时候总是不能正常连接,LDAP地址只能写host,后面不能跟DN,否则就连不上服务器,而且这种方法连接上服务器也不能检索用户。后来改为AuthenticationTypes.FastBind之后才能正常工作了。
1 //---------------------------------------------------------------------------------------------- 2 // DirectoryEntry 方案, 需要引用 System.DirectoryServices 3 //---------------------------------------------------------------------------------------------- 4 var ldapPath = "LDAP://" + host + "/" + baseDN; // LDAP必须要大写,好像是.NET的特色 5 DirectoryEntry de = new DirectoryEntry(ldapPath, adminName, adminPass, AuthenticationTypes.FastBind); 6 DirectorySearcher searcher = new DirectorySearcher(de); 7 searcher.Filter = "(uid=" + testUser + ")"; 8 searcher.SearchScope = SearchScope.Subtree; 9 searcher.PropertiesToLoad.Add("uid"); 10 searcher.PropertiesToLoad.Add("cn"); 11 12 var result = searcher.FindOne(); 13 14 // 输出几个查询的属性值 15 foreach (string n in result.Properties.PropertyNames) 16 { 17 Console.WriteLine("{0}: {1}", n, result.Properties[n][0].ToString()); 18 } 19 20 try 21 { 22 int pos = result.Path.LastIndexOf('/'); 23 string uid = result.Path.Remove(0, pos + 1); 24 25 // 二次连接,使用需要认证的用户密码尝试连接 26 DirectoryEntry deUser = new DirectoryEntry(ldapPath, uid, testPass, AuthenticationTypes.FastBind); 27 var connected = deUser.NativeObject; 28 29 Console.WriteLine("### 认证成功!"); 30 } 31 catch 32 { 33 Console.WriteLine("认证失败~~~"); 34 }
另外一种方案是我同事找到的,和我上面一种方案几乎在同一时间找到,比较坑,是使用.NET官方类库中的LdapConnection,我一直认为LDAP这么常见的东西一定有官方的解决方案,奈何搜遍了国内外的中文、E文网站,“LDAP C#”、“LDAP .NET”关键字都搜了,就是没有任何人提到关于这个类的片言只字,真无语!难道这玩意就这么冷门吗?难道大家都在用DirectoryEntry吗?不可思议。
1 //------------------------------------------------------------------------------------------ 2 // LdapConnection 方案, 需要引用 System.DirectoryServices.Protocols 3 //------------------------------------------------------------------------------------------ 4 var identifier = new LdapDirectoryIdentifier(host); 5 var conn = new LdapConnection(identifier, new NetworkCredential 6 { 7 UserName = adminName, 8 Password = adminPass 9 }); 10 conn.AuthType = AuthType.Basic; 11 conn.Bind(); 12 13 var request = new SearchRequest(baseDN, "(uid=" + testUser + ")", SearchScope.Subtree, "otherPassword"); 14 SearchResponse response = conn.SendRequest(request) as SearchResponse; 15 if (response.Entries != null && response.Entries.Count > 0) 16 { 17 try 18 { 19 var connUser = new LdapConnection(identifier, new NetworkCredential 20 { 21 UserName = response.Entries[0].DistinguishedName, 22 Password = testPass 23 }); 24 connUser.AuthType = AuthType.Basic; 25 connUser.Bind(); 26 27 Console.WriteLine("### 认证成功!"); 28 } 29 catch 30 { 31 Console.WriteLine("认证失败~~~ error password"); 32 } 33 } 34 else 35 { 36 Console.WriteLine("认证失败~~~ no user"); 37 }
测试代码中用到的一些变量声明:
1 var host = "xxx.xxx.xxx.xxx:389"; 2 var baseDN = "dc=xxx,dc=xxx,dc=com"; 3 var adminName = "uid=管理账号,ou=管理组," + baseDN; 4 var adminPass = "管理密码"; 5 var testUser = "测试认证用户账号"; 6 var testPass = "测试认证用户密码";