概要
活動目錄(Active Directory)是面向Windows Standard Server、Windows Enterprise Server以及 Windows Datacenter Server的目錄服務。(Active Directory不能運行在Windows Web Server上,但是可以通過它對運行Windows Web Server的計算機進行管理。)Active Directory存儲了有關網絡對象的信息,並且讓管理員和用戶能夠輕松地查找和使用這些信息。Active Directory使用了一種結構化的數據存儲方式,並以此作為基礎對目錄信息進行合乎邏輯的分層組織。
Microsoft Active Directory 服務是Windows 平台的核心組件,它為用戶管理網絡環境各個組成要素的標識和關系提供了一種有力的手段。
功能
活動目錄(Active Directory)主要提供以下功能:
①基礎網絡服務:包括DNS、WINS、DHCP、證書服務等。
②服務器及客戶端計算機管理:管理服務器及客戶端計算機賬戶,所有服務器及客戶端計算機加入域管理並實施組策略。
③用戶服務:管理用戶域賬戶、用戶信息、企業通訊錄(與電子郵件系統集成)、用戶組管理、用戶身份認證、用戶授權管理等,按省實施組管理策略。
④資源管理:管理打印機、文件共享服務等網絡資源。
⑤桌面配置:系統管理員可以集中的配置各種桌面配置策略,如:界面功能的限制、應用程序執行特征限制、網絡連接限制、安全配置限制等。
⑥應用系統支撐:支持財務、人事、電子郵件、企業信息門戶、辦公自動化、補丁管理、防病毒系統等各種應用系統。
LDAP
LDAP是輕量目錄訪問協議,英文全稱是Lightweight Directory Access Protocol。
LDAP是基於X.500標准的。
LDAP 僅通過使用原始 X.500目錄存取協議 (DAP) 的功能子集而減少了所需的系統資源消耗。
與X.500不同,LDAP支持TCP/IP,這對訪問Internet是必須的。
LDAP和關系數據庫是兩種不同層次的概念,后者是存貯方式(同一層次如網格數據庫,對象數據庫),前者是存貯模式和訪問協議。
LDAP是一個比關系數據庫抽象層次更高的存貯概念,與關系數據庫的查詢語言SQL屬同一級別。
開發功能
先看看System.DirectoryServices的代碼
namespace System.DirectoryServices { [DSDescription("DirectoryEntryDesc")] [TypeConverter(typeof (DirectoryEntryConverter))] [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)] [DirectoryServicesPermission(SecurityAction.LinkDemand, Unrestricted = true)] public class DirectoryEntry : Component { [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted = true)] public DirectoryEntry(); [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted = true)] public DirectoryEntry(string path); [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted = true)] public DirectoryEntry(string path, string username, string password); [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted = true)] public DirectoryEntry(string path, string username, string password, AuthenticationTypes authenticationType); [DirectoryServicesPermission(SecurityAction.Demand, Unrestricted = true)] public DirectoryEntry(object adsObject); [DefaultValue(AuthenticationTypes.Secure)] [DSDescription("DSAuthenticationType")] public AuthenticationTypes AuthenticationType { get; set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [DSDescription("DSChildren")] [Browsable(false)] public DirectoryEntries Children { get; } [DSDescription("DSGuid")] [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Guid Guid { get; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] [DSDescription("DSObjectSecurity")] public ActiveDirectorySecurity ObjectSecurity { get; set; } [DSDescription("DSName")] [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string Name { get; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [DSDescription("DSNativeGuid")] [Browsable(false)] public string NativeGuid { get; } [DSDescription("DSNativeObject")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] public object NativeObject { get; } [DSDescription("DSParent")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] public DirectoryEntry Parent { get; } [DefaultValue(null)] [Browsable(false)] [DSDescription("DSPassword")] public string Password { set; } [SettingsBindable(true)] [DefaultValue("")] [TypeConverter("System.Diagnostics.Design.StringValueConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] [DSDescription("DSPath")] public string Path { get; set; } [DSDescription("DSProperties")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] public PropertyCollection Properties { get; } [DSDescription("DSSchemaClassName")] [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public string SchemaClassName { get; } [Browsable(false)] [DSDescription("DSSchemaEntry")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public DirectoryEntry SchemaEntry { get; } [DefaultValue(true)] [DSDescription("DSUsePropertyCache")] public bool UsePropertyCache { get; set; } [DSDescription("DSUsername")] [DefaultValue(null)] [Browsable(false)] [TypeConverter("System.Diagnostics.Design.StringValueConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] public string Username { get; set; } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [ComVisible(false)] [DSDescription("DSOptions")] [Browsable(false)] public DirectoryEntryConfiguration Options { get; } public void Close(); public void CommitChanges(); public DirectoryEntry CopyTo(DirectoryEntry newParent); public DirectoryEntry CopyTo(DirectoryEntry newParent, string newName); public void DeleteTree(); protected override void Dispose(bool disposing); public static bool Exists(string path); public object Invoke(string methodName, params object[] args); [ComVisible(false)] public object InvokeGet(string propertyName); [ComVisible(false)] public void InvokeSet(string propertyName, params object[] args); public void MoveTo(DirectoryEntry newParent); public void MoveTo(DirectoryEntry newParent, string newName); public void RefreshCache(); public void RefreshCache(string[] propertyNames); public void Rename(string newName); } }
在Active Directory中搜索
1.SearchRoot
指定搜索從哪里開始,如當前節點等
DirectorySearcher search = new DirectorySearcher(); search.SearchRoot = de;
2.過濾器
過濾條件是用雙引號引起來的括號中的內容如"(&(objectClass=user)(|(description=Auth*)(name=m*)))"
searcher.Filter="(&(objectClass=user)(|(description=Auth*)(name=m*)))"
3.搜索范圍
search.SearchScope = SearchScope.Subtree;
取值如下:Subtree,Base(只搜索對象中的屬性,至多可以得到一個對象),OneLevel(在基對象的子集中搜索,基對象不搜索)
4.加載的屬性PropertiesToLoad
對象的很多屬性都不太重要,此處定義了加載到緩存中的對象屬性,若沒有指定,默認是對象的Path和Name屬性
search.PropertiesToLoad.Add("name"); search.PropertiesToLoad.Add("description"); search.PropertiesToLoad.Add("giveName"); search.PropertiesToLoad.Add("wWWWHomePage");
5.對結果進行排序,Sort函數有兩個參數,第一個是要排序的字段,第二個為排序方式SortOption有兩個值,Ascending和Descending
search.Sort = new SortOption("givenName",SortDirection.Ascending);
6.開始搜索,FindAll()查找返回一個SearchResultCollection,FindOne()返回一個簡單的SearchResult對象
SearchResultCollection results = searcher.FindAll();
根據用戶帳號取得AD USER Information
/// <summary> /// 根據用戶賬號取得AD User對象 /// </summary> /// <param name="account"></param> /// <returns></returns> public DirectoryEntry GetUserByAccount(string account) { DirectoryEntry user = null; using (DirectoryEntry entry = new DirectoryEntry(this.LDAPAddress, this.AdminAccount, this.AdminPassword, AuthenticationTypes.Secure)) { using (DirectorySearcher searcher = new DirectorySearcher(entry)) { searcher.Filter = "(&(objectClass=user)(sAMAccountName=" + account + "))"; searcher.SearchScope = SearchScope.Subtree; SearchResult searchResult = searcher.FindOne(); if (searchResult != null) { user = searchResult.GetDirectoryEntry(); } } entry.Close(); } return user; }
LDAPAddress的值
LDAP://192.168.3.107/DC=xxxx,DC=xxxx
判斷用戶名字是否存在
//判斷用戶名字是否存在 [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true, PreserveSig = true)] private static extern bool LookupAccountName( string lpSystemName, string lpAccountName, System.IntPtr psid, ref int cbsid, StringBuilder domainName, ref int cbdomainLength, ref int use); public bool LookUpAccount(string accountName) { //pointer an size for the SID IntPtr sid = IntPtr.Zero; int sidSize = 0; //StringBuilder and size for the domain name StringBuilder domainName = new StringBuilder(); int nameSize = 0; //account-type variable for lookup int accountType = 0; //get required buffer size LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); //allocate buffers domainName = new StringBuilder(nameSize); sid = Marshal.AllocHGlobal(sidSize); //lookup the SID for the account bool result = LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType); if (result) { if (accountName.ToLower().IndexOf(domainName.ToString().ToLower()) < 0) { accountName = domainName + "\\" + accountName; } //throw.Exception; .Show("The account is : " + accountName); } else { //MessageBox.Show("Can't find the account."); } Marshal.FreeHGlobal(sid); return result; } }
用戶賬號是否已處於活動狀態(非禁用)
/// <summary>
/// 用戶賬號是否已處於活動狀態(非禁用)
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
private bool IsUserActive(DirectoryEntry user)
{
int val = (int)user.Properties["userAccountControl"].Value;
int flag = (int)(0x0002);
int flagExists = val & flag;
if (flagExists > 0)
return false;
else
return true;
}
用戶登陸的實現
/// <summary> /// 用戶登錄驗證 /// </summary> /// <param name="account">AD賬號</param> /// <param name="password">密碼</param> /// <returns> /// <para>True:驗證成功</para> /// <para>False:驗證失敗,如果this.Error不為空,則為異常信息</para> /// </returns> public ADLoginResult Login(string account, string password) { ADLoginResult result = ADLoginResult.UnSuccess; /* Check Accout Empty */ if (string.IsNullOrEmpty(account)) { result = ADLoginResult.UnSuccess; return result; } DirectoryEntry entry = null; DirectoryEntry user = null; try { /* Check Account Exists */ entry = this.GetUserByAccount(account); if (entry != null) { using (entry) { /* Check Account Active */ if (this.IsUserActive(entry)) { /* Check Password */ using (user = new DirectoryEntry(this.LDAPAddress, account, password, AuthenticationTypes.Secure)) { object obj = user.NativeObject; user.Close(); } user = null; result = ADLoginResult.Success; } else { result = ADLoginResult.Disabled; } entry.Close(); } } else { result = ADLoginResult.UnSuccess; } } catch (Exception ex) { this.Error = ex; } finally { if (user != null) { user.Close(); user.Dispose(); } if (entry != null) { entry.Close(); entry.Dispose(); } } return result; }
根據域密碼壽命策略計算密碼過期時間
/// <summary> /// 根據域密碼壽命策略計算密碼過期時間 /// </summary> /// <param name="dcEntry">活動目錄的域節點</param> /// <param name="user">活動目錄的用戶節點</param> /// <returns></returns> private DateTime? InnerGetPasswordExpirationDate(DirectoryEntry user) { /* * http://msdn.microsoft.com/library/en-us/dnclinic/html/scripting09102002.asp */ bool isPasswordNotExpire = ((int)user.Properties["userAccountControl"].Value & (int)ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD) != 0; if (isPasswordNotExpire) { return DateTime.MaxValue;/* 帳號被設置為密碼永不過期 */ } else { long lastChanged; try { lastChanged = GetLongValue((IADsLargeInteger)user.Properties["pwdLastSet"][0]); } catch (Exception) { return DateTime.MinValue; /* 密碼沒有被設置過 */ } #region [ 是否啟用域密碼壽命策略(暫時無法獲得) ] #endregion #region [ 域密碼壽命策略 ] DirectoryEntry dcEntry = null; try { dcEntry = new DirectoryEntry(this.LDAPAddress, this.AdminAccount, this.AdminPassword); dcEntry.UsePropertyCache = false; dcEntry.RefreshCache(new string[] { "maxPwdAge" }); IADsLargeInteger maxAge = (IADsLargeInteger)dcEntry.Properties["maxPwdAge"][0]; if (maxAge.LowPart == 0) { /* * 注意: * 1)當域從未啟用過密碼壽命策略時,該maxAge.LowPart=0,即域中密碼沒有設置最大有效期限 * 2)但是當啟用過密碼壽命策略以后,即使不再啟用該策略,但域的屬性[maxPwdAge]仍保留着之前的值。 * 3)當在域服務器上修改該策略后,仍要等約10分鍾后才能讀取到修改后的值。 * 所以對於啟用過密碼壽命策略的域來講,不會走以下代碼段。 */ return DateTime.MaxValue; } else { long maxAge1 = GetLongValue((IADsLargeInteger)dcEntry.Properties["maxPwdAge"][0]); int serverMaxPwdDays = -(int)(maxAge1 / ONE_HUNDRED_NANOSECOND / SECONDS_IN_DAY);/* 密碼壽命 */ DateTime passwordLastChanged = DateTime.FromFileTime(GetLongValue((IADsLargeInteger)user.Properties["pwdLastSet"][0]));/* 員工密碼上次修改時間 */ return passwordLastChanged.AddDays(serverMaxPwdDays); } dcEntry.Close(); dcEntry.Dispose(); dcEntry = null; } catch (Exception ex) { this.Error = ex; return null; } finally { if (dcEntry != null) { dcEntry.Close(); dcEntry.Dispose(); } } #endregion } }
修改用戶密碼
/// <summary> /// 修改用戶密碼 /// </summary> /// <param name="account">AD賬號</param> /// <param name="oldPassword">舊密碼</param> /// <param name="newPassword">新密碼</param> /// <returns> /// <para>ChangePwdResult.Success::修改成功</para> /// <para>其它:修改失敗,如果this.Error不為空,則為異常信息</para> /// </returns> public ChangePwdResult ChangePassword(string account, string oldPassword, string newPassword) { ChangePwdResult result = ChangePwdResult.ChangePwdUnSuccess; /* Check Accout Empty */ if (string.IsNullOrEmpty(account)) { return ChangePwdResult.UserNotExists; } DirectoryEntry user = null; /* Check Login */ ADLoginResult loginSuccess = this.Login(account, oldPassword); try { switch (loginSuccess) { case ADLoginResult.Success: #region [ Success ] user = this.GetUserByAccount(account); if (user != null) { using (user) { bool bImpSuccess = false; if (this.IsUseImpersonate) { bImpSuccess = this._impersonation.BeginImpersonate(); this.ImpsersonateSuccess = bImpSuccess; if (!bImpSuccess) { /* * 打開模擬情況下模擬失敗,先將結果默認為AdminLoginUnSuccess; * 如果密碼修改成功,則會更新結果為Success;如果異常, */ result = ChangePwdResult.AdminLoginUnSuccess; } } int userAccountControl = Convert.ToInt32(user.Properties["userAccountControl"][0]); /* Change Password */ user.Invoke("SetPassword", new object[] { newPassword }); user.CommitChanges(); if (this.IsUseImpersonate && bImpSuccess) { this._impersonation.StopImpersonate(); } user.Close(); result = ChangePwdResult.Success; } user = null; } else { result = ChangePwdResult.UserNotExists; } #endregion break; case ADLoginResult.UnSuccess: result = ChangePwdResult.WrongOldPwd; break; case ADLoginResult.Disabled: result = ChangePwdResult.Disabled; break; default: result = ChangePwdResult.ChangePwdUnSuccess; break; } } catch (Exception ex) { /* 模擬失敗情況下,返回模擬失敗 */ if (result != ChangePwdResult.AdminLoginUnSuccess) { result = ChangePwdResult.ChangePwdUnSuccess; } this.Error = ex;/* Get Error */ } finally { if (user != null) { user.Close(); user.Dispose(); } } return result; }
總結
AD開發主要用於SSO(單點登陸的開發),在office communication 開發中會用到.希望能對大家有幫助.
參考文獻
1、http://baike.baidu.com/view/41408.htm
歡迎各位參與討論,如果覺得對你有幫助,請點擊 推薦下,萬分謝謝.
作者:spring yang
出處:http://www.cnblogs.com/springyangwc/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。