Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學

使用 .NET Framework System.DirectoryServices 存取 Active Directory (上)

編輯:Windows Server 教學

前言:.NET 時代的目錄服務程式設計

.NET Framework 透過 System.DirectoryServices 命名空間,將 ADSI(Active Directory Service Interfaces)包裝為 Managed 程式碼可直接呼叫的類別,讓 ASP.NET 與 WinForm 應用程式能輕鬆實作使用者管理、查詢、群組維護等目錄操作。本系列文章將建立一個可複用的資料存取層 (DAL),封裝核心邏輯,並示範 ASP.NET 管理頁面的實作。

一、 Active Directory 快速回顧

  • 本質: 階層式、物件導向、分散式目錄資料庫。頂層為 Organization (O),下層為 Organizational Unit (OU/容器),葉節點為實際資源物件(User、Computer、Printer 等)。
  • 權限繼承: 對 OU 設定的權限會自動繼承給其包含的所有子物件。
  • 協議支援: ADSI 透過不同提供者存取多種目錄:
提供者路徑前綴適用環境
Windows NT 5.0/2000/XPWinNT://本機 SAM、NT 4.0 網域
LDAP (Active Directory)LDAP://Windows 2000/2003 AD、LDAP v3 相容目錄
Novell NDSNDS://Novell eDirectory
Novell NetWare 3.xNWCOMPAT://舊版 NetWare
IIS MetabaseIIS://Internet Information Services

注意: DirectorySearcher 僅支援 LDAP 提供者;DirectoryEntry 則支援所有提供者。

二、 核心類別速覽

  • DirectoryEntry:代表目錄樹中的單一節點(物件)。每個物件具備 Schema 定義的屬性集合(如 User 物件含 cn、sn、mail、userPassword 等)。
  • DirectorySearcher:針對 LDAP 提供者執行高效能查詢,支援篩選條件、屬性選取、分頁、排序。

三、 設計資料存取層 (DAL) - UserAdmin 元件

DAL 目的:隱藏 ADSI/ADO.NET 複雜度,提供乾淨的物件導向介面,未來若切換資料來源(如改用 SQL Server),僅需修改 DAL 實作,上層呼叫端零變動。

1. 領域物件:DSUser

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; }
        }
    }
}

2. DAL 核心類別:UserAdmin

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 等方法
}

四、 關鍵設計決策說明

  1. 錯誤處理策略: 採用 StringBuilder 累積錯誤日誌 + Boolean Error 標記,避免例外拋出造成呼叫端中斷,適合 Web 應用在頁面顯示友善錯誤訊息。
  2. 提供者抽象化: loginPath 可由建構子注入,預設為 WinNT 本機,實際部署時應改為 LDAP:/// 以支援網域管理。
  3. Invoke 動態呼叫: WinNT 提供者不實作強型別屬性存取,必須透過 DirectoryEntry.Invoke("Get", "PropertyName") 取值;LDAP 提供者則可直接使用 entry.Properties["attr"][0],效能較佳。
  4. 安全性考量: Connect() 僅建立連線,實際寫入操作 (SaveUser, DeleteUser) 應搭配 模擬 機制以特定高權限帳號執行,避免 ASP.NET 處理序帳號過度授權。

下集預告

下一篇將完整實作 DAL 的「動作方法」:LoadUser、AddUserToGroup、GetGroups、DeleteUser、SaveUser,並示範如何在 ASP.NET 頁面中整合 Windows 身分模擬 與 machine.config 設定,打造功能完整的使用者管理介面。

Copyright © Windows教程網 All Rights Reserved