.NET Framework 透過 System.DirectoryServices 命名空間,將 ADSI(Active Directory Service Interfaces)包裝為 Managed 程式碼可直接呼叫的類別,讓 ASP.NET 與 WinForm 應用程式能輕鬆實作使用者管理、查詢、群組維護等目錄操作。本系列文章將建立一個可複用的資料存取層 (DAL),封裝核心邏輯,並示範 ASP.NET 管理頁面的實作。
| 提供者 | 路徑前綴 | 適用環境 |
|---|---|---|
| Windows NT 5.0/2000/XP | WinNT:// | 本機 SAM、NT 4.0 網域 |
| LDAP (Active Directory) | LDAP:// | Windows 2000/2003 AD、LDAP v3 相容目錄 |
| Novell NDS | NDS:// | Novell eDirectory |
| Novell NetWare 3.x | NWCOMPAT:// | 舊版 NetWare |
| IIS Metabase | IIS:// | Internet Information Services |
注意: DirectorySearcher 僅支援 LDAP 提供者;DirectoryEntry 則支援所有提供者。
DAL 目的:隱藏 ADSI/ADO.NET 複雜度,提供乾淨的物件導向介面,未來若切換資料來源(如改用 SQL Server),僅需修改 DAL 實作,上層呼叫端零變動。
namespace DSHelper {
public class DSUser {
// 屬性對應 AD User 物件常用屬性
public string DomainName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public string Description { get; set; }
public int PasswordExpired { get; set; }
public int RasPermissions { get; set; }
public int MaxStorage { get; set; }
public int PasswordAge { get; set; }
public string HomeDirectory { get; set; }
public string LoginScript { get; set; }
public string HomeDirDrive { get; set; }
public object Groups { get; set; } // 群組成員資訊
internal DirectoryEntry userDirEntry; // 內部保留 DirectoryEntry 參考
// 建構子 1:從現有 DirectoryEntry 填充
public DSUser(DirectoryEntry user) {
this.DomainName = user.Path;
this.Username = user.Name;
// 透過 Invoke 取得屬性 (WinNT 提供者不支援強型別屬性存取)
this.FullName = GetProperty(user, "FullName");
this.Description = GetProperty(user, "Description");
this.PasswordExpired = GetIntProperty(user, "PasswordExpired");
this.RasPermissions = GetIntProperty(user, "RasPermissions");
this.MaxStorage = GetIntProperty(user, "MaxStorage");
this.PasswordAge = GetIntProperty(user, "PasswordAge");
this.HomeDirectory = GetProperty(user, "HomeDirectory");
this.LoginScript = GetProperty(user, "LoginScript");
this.HomeDirDrive = GetProperty(user, "HomeDirDrive");
this.userDirEntry = user;
}
// 建構子 2:建立新使用者時使用 (尚未寫入 AD)
public DSUser(string username, string password, string domainName) {
Username = username; Password = password; DomainName = domainName;
}
private string GetProperty(DirectoryEntry entry, string name) {
try { return Convert.ToString(entry.Invoke("Get", new object[] { name })); }
catch { return string.Empty; }
}
private int GetIntProperty(DirectoryEntry entry, string name) {
try { return Convert.ToInt32(entry.Invoke("Get", new object[] { name })); }
catch { return 0; }
}
}
}
public class UserAdmin {
#region 內部欄位與屬性
private StringBuilder errorLog = new StringBuilder();
private bool error = false;
public bool Error { get { return error; } }
public string ErrorLog { get { return errorLog.ToString(); } }
// 連線設定
private string loginPath = "WinNT://" + Environment.MachineName + ",computer";
private string domainName = null;
private string loginUsername = null;
private string loginPassword = null;
private AuthenticationTypes authType = AuthenticationTypes.None;
private DirectoryEntry rootEntry = null;
private bool connected = false;
#endregion
#region 建構子
// 預設:匿名繫結本機 WinNT
public UserAdmin() { Connect(); }
// 指定認證與網域
public UserAdmin(string loginUser, string loginPwd, AuthenticationTypes auth, string domain) {
loginUsername = loginUser; loginPassword = loginPwd; authType = auth;
domainName = string.IsNullOrEmpty(domain) ? Environment.UserDomainName : domain;
Connect();
}
// 完整控制:自訂 LoginPath (可切換 LDAP/IIS/NDS)
public UserAdmin(string loginPath, string loginUser, string loginPwd, AuthenticationTypes auth, string domain) {
this.loginPath = loginPath;
loginUsername = loginUser; loginPassword = loginPwd; authType = auth;
domainName = string.IsNullOrEmpty(domain) ? Environment.UserDomainName : domain;
Connect();
}
#endregion
#region 核心連線方法
public bool Connect() {
try {
rootEntry = new DirectoryEntry(loginPath, loginUsername, loginPassword, authType);
// 強制繫結驗證憑證
object native = rootEntry.NativeObject;
connected = true;
return true;
} catch (Exception ex) {
LogError("Connect failed: " + ex.Message);
connected = false;
return false;
}
}
#endregion
// ... 後續文章將實作 LoadUser, AddUserToGroup, GetGroups, DeleteUser, SaveUser 等方法
}
StringBuilder 累積錯誤日誌 + Boolean Error 標記,避免例外拋出造成呼叫端中斷,適合 Web 應用在頁面顯示友善錯誤訊息。loginPath 可由建構子注入,預設為 WinNT 本機,實際部署時應改為 LDAP:/// 以支援網域管理。DirectoryEntry.Invoke("Get", "PropertyName") 取值;LDAP 提供者則可直接使用 entry.Properties["attr"][0],效能較佳。Connect() 僅建立連線,實際寫入操作 (SaveUser, DeleteUser) 應搭配 模擬 機制以特定高權限帳號執行,避免 ASP.NET 處理序帳號過度授權。下一篇將完整實作 DAL 的「動作方法」:LoadUser、AddUserToGroup、GetGroups、DeleteUser、SaveUser,並示範如何在 ASP.NET 頁面中整合 Windows 身分模擬 與 machine.config 設定,打造功能完整的使用者管理介面。