LDAP – How to Query User by SID(Security Identifier)
最近碰到一個功能,要利用SID反查登入者的帳號和網域;以前曾經寫過類似的登入驗證程式,不過已經是好遙久的記憶了,所以今天又重新復習了一下。
什麼是LDAP呢?
LDAP其實算是一種透過TCP/IP查詢的目錄服務,全名是:Lightweight Directory Access Protocol,我們可以透過他來存取有支援的控管系統,例如:微軟的Activity Directory、Exchange Server。
在.Net Framework中的System.DirectoryServices命名空間下,提供了LDAP查詢服務,還可以利用DirectorySearcher來做進一步的篩選動作;不過他的查詢條件和我們一般使用的不太一樣,是類似二元樹的中序排列方式。DirectorySearcher可以指定的查詢的條件有很多,最常用的大概就是objectClass來做基本的過濾。
LDAP的連線字串:LDAP://Server/DN,其中DN包含了三種東西:
CN:Common Name
OU:Organizational Unit
DC:Domain Component
我們一般看到的DN字串:CN=Shelly, OU=RD, DC=company, DC=com,表示在RD(OU)這個部門底下有一個叫Shelly(CN)的人存在,在 LDAP 的表示方法都是由小到大,也就是人名先表示、再表示部門、單位。透過LDAP,我們可以查詢使用者登入的ID、EMAIL、住址等等存在AD Server上的資料;要查詢之前,我們必須要知道Domain Server位置,以及具有瀏覽權限的帳號密碼。
理論的東西就麻煩大家自己再去看囉!以下是一個簡單的查詢所有使用者的範例:
DirectoryEntry searchRoot = new DirectoryEntry(LDAP, account, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(&(objectClass=user)(objectClass=person))(!objectClass=computer))";
SearchResultCollection resultCol = search.FindAll();
foreach (SearchResult item in resultCol)
{
var s = item.GetDirectoryEntry().Properties["objectSid"].Value as byte[];
var name = item.GetDirectoryEntry().Properties["cn"].Value as string;
SecurityIdentifier si = new SecurityIdentifier(s, 0);
si.ToString(); //S-1-5-21-1099441283-3967551234-751658072-8888
//....以下省略
}
那SID又是什麼呢?SID是一個Security Identifiers,當你建立帳號的時候,Active Directory會自動指派一個SID給這個帳號,這個值是唯一不可重覆的,透過LDAP查詢SID的方式有二種,一個直接在連線字串指定,另外一個是在之後利用DirectorySearcher指定查詢字串。在AD中SID的資料是以二進位格式存放,我們可以透過SecurityIdentifier類別來進行轉換。
連線字串指定:(註:如果查不到該物件,存取的時候會發生物件不存在的Exception)
DirectoryEntry de = new DirectoryEntry(LDAP, account, password);
try
{
var s = de.Properties["objectSid"].Value as byte[];
var name = de.Properties["cn"].Value as string;
SecurityIdentifier si = new SecurityIdentifier(s, 0);
si.ToString(); // S-1-5-21-1099441555-3967551234-751658072-502
}
catch (Exception)
{
}
DirectorySearcher指定查詢字串:objectSid
DirectoryEntry searchRoot = new DirectoryEntry(LDAP, account, password);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = String.Format("(objectSid={0})", sid);
search.PropertiesToLoad.Add("objectSid");
search.PropertiesToLoad.Add("cn");
SearchResultCollection resultCol = search.FindAll();
foreach (SearchResult item in resultCol)
{
var s = item.GetDirectoryEntry().Properties["objectSid"].Value as byte[];
var name = item.GetDirectoryEntry().Properties["cn"].Value as string;
SecurityIdentifier si = new SecurityIdentifier(s, 0);
//....
}
我小測了一下(存取20筆的SID),第二個方法會比第一個方法快速很多!
相關連結:
關於 AD (Active Directory) / LDAP 開發的相關資訊
How to write a LDAP search filter
[MMDays專欄] 由樹的前序、中序、後序走法來談資料結構