Active Directory 开发
概要
活动目录(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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。