最近由於項目需要研究了一下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 = "測試認證用戶密碼";