码迷,mamicode.com
首页 > 其他好文 > 详细

AD帐户操作C#示例代码(二)——检查密码将过期的用户

时间:2014-08-07 22:01:16      阅读:468      评论:0      收藏:0      [点我收藏+]

标签:blog   http   使用   os   io   文件   数据   for   

本文接着和大家分享AD帐户操作,这次开发一个简单的检查密码将过期用户的小工具。

bubuko.com,布布扣

     首先,新建一个用户实体类,属性是我们要取的用户信息。

bubuko.com,布布扣
bubuko.com,布布扣
    public class UserInfo
    {
        /// <summary>
        /// sAM帐户名称
        /// </summary>
        public string SamAccountName { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        public string Mail { get; set; }
        /// <summary>
        /// 已禁用
        /// </summary>
        public bool IsDisabled { get; set; }
        /// <summary>
        /// 设置为密码永不过期
        /// </summary>
        public bool IsPasswordNeverExpire { get; set; }
        /// <summary>
        /// 设置为不需要密码
        /// </summary>
        public bool IsNoPasswordRequired { get; set; }
        /// <summary>
        /// 系统密码过期设置天数
        /// </summary>
        public long MaxPasswordAge { get; set; }
        /// <summary>
        /// 剩余过期天数
        /// </summary>
        public double? SurplusPasswordExpirationDays {
            get 
            {
                if (!PasswordExpirationDate.HasValue)
                {
                    return default(double?);
                }
                double days = PasswordExpirationDate.Value.Subtract(DateTime.Now).TotalDays;
                if (days <= 0)
                {
                    return 0;
                }
                return Math.Round(days, 2); 
            }
        }
        /// <summary>
        /// 最近密码修改时间
        /// </summary>
        public DateTime PasswordLastSet { get; set; }
        /// <summary>
        /// 密码过期时间
        /// </summary>
        public DateTime? PasswordExpirationDate { get; set; }
    }
bubuko.com,布布扣

     然后是搜索用户信息的方法。

bubuko.com,布布扣
private IEnumerable<UserInfo> SearchUsers(string path, string username, string password, string sAMAccountName, string displayName, bool isDisabled, bool IsPasswordNeverExpire, long[] surplusExpirationDays)
        {
            using (DirectoryEntry directoryEntry = new DirectoryEntry(path, username, password))
            {
                using (DirectorySearcher directorySearcher = new DirectorySearcher(directoryEntry, @"&(objectCategory=person)(objectClass=user))", new string[] { "name", "sAMAccountName", "userAccountcontrol", "pwdLastSet", "mail" }, SearchScope.Subtree) { PageSize = 1000 })
                {
                    using (SearchResultCollection userResultCollection = directorySearcher.FindAll())
                    {
                        foreach (SearchResult userResult in userResultCollection)
                        {
                            UserInfo userInfo = new UserInfo();
                            //TODO: 赋值
                            yield return userInfo;
                        }
                    }
                }
            }
        }
bubuko.com,布布扣

     这次我们主要用DirectorySearcher类:SearchRoot是搜索的DirectoryEntry根节点;SearchScope属性是搜索的范围,是个SearchScope枚举:Base(限于基对象)、OneLevel(搜索基对象的直接子对象,但不搜索基对象)、Subtree(搜索整个子树,包括基对象及其所有子对象)。我们要在指定的OU下搜索用户,所以选择子树Subtree。

     DirectorySearcher类的Filter属性是过滤条件,搜索用户就是“&(objectCategory=person)(objectClass=user))"。注意:表示与或非的“&”、“|”、“!”要放在这些条件表达式前面而不是它们之间;如果要做模糊查询用通配符*;可以用“=”、“>=”、“<=”、“~=”(约等于),但“>”和”<“是不行的;”pwdLastSet“要转为Windows文件时间,因为存的是个long,还有处理”userAccountControl"的并运算,这里用“:1.2.840.113556.1.4.803:=”。我们可以把一些查询条件放在Filter里,减少搜索结果的返回行数:

bubuko.com,布布扣
                    directorySearcher.SearchScope = SearchScope.Subtree;
                    List<string> filterItems = new List<string>();
                    if (!string.IsNullOrEmpty(sAMAccountName))
                    {
                        filterItems.Add(string.Format(@"(sAMAccountName={0})", sAMAccountName));
                    }
                    if (!string.IsNullOrEmpty(displayName))
                    {
                        filterItems.Add(string.Format(@"(name={0})", displayName));
                    }
                    if (!containsDisabled)
                    {
                        filterItems.Add(@"(!(userAccountControl:1.2.840.113556.1.4.803:=2))");
                    }
                    if (!containsPasswordNeverExpire)
                    {
                        filterItems.Add(@"(!(userAccountControl:1.2.840.113556.1.4.803:=65536))");
                    }
                    if (!containsNoPasswordRequired)
                    {
                        filterItems.Add(@"(!(userAccountControl:1.2.840.113556.1.4.803:=32))");
                    }
                    if (surplusExpirationDays != null && surplusExpirationDays.Length > 0)
                    {
                        StringBuilder surplusExpirationDaysFilter = new StringBuilder(@"(|");
                        DateTime now = DateTime.Now;
                        foreach (long surplusExpirationDay in surplusExpirationDays)
                        {
                            DateTime passwordExpirationDate = now.AddDays(surplusExpirationDay);
                            DateTime passwordLastSet = passwordExpirationDate.AddDays(-1 * maxPwdAge);
                            if (surplusExpirationDay != 0)
                            {
                                surplusExpirationDaysFilter.AppendFormat("(&(pwdLastSet>={0})(pwdLastSet<={1}))", passwordLastSet.ToFileTime().ToString(), passwordLastSet.AddDays(1).AddSeconds(-1).ToFileTime().ToString());
                            }
                            else
                            {
                                surplusExpirationDaysFilter.AppendFormat("(pwdLastSet<={0})(pwdLastSet=0)", passwordLastSet.AddDays(1).AddSeconds(-1).ToFileTime().ToString());
                            }                        }
                        surplusExpirationDaysFilter.Append(@")");
                        filterItems.Add(surplusExpirationDaysFilter.ToString());
                    }
                    directorySearcher.Filter = string.Format(@"(&{0}(objectCategory=person)(objectClass=user))", string.Concat(filterItems));
bubuko.com,布布扣

     Filter语法请参考:http://msdn.microsoft.com/en-us/library/aa746475.aspxhttp://www.ldapexplorer.com/en/manual/109010000-ldap-filter-syntax.htm

     userAccountControl标志请参考:http://support.microsoft.com/kb/305144/zh-cnhttp://msdn.microsoft.com/zh-cn/library/ms680832(VS.85).aspxhttp://technet.microsoft.com/library/ee198831.aspx

     DirectorySearcher类的PropertiesToLoad属性是要检索的属性列表,这个就相当于我们访问数据库时写SQL语句里SELECT后面的东西,最好按需指定,尽量不写“SELECT *”; 注意DirectorySearcher类的PageSize属性,如果要返回所有数据可以设为1000,默认是只返回1000条的。

 directorySearcher.PropertiesToLoad.AddRange(new string[] { "name", "sAMAccountName", "userAccountcontrol", "pwdLastSet", "mail" });
 directorySearcher.PageSize = 1000;

      更多DirectorySearcher类属性请参考:http://msdn.microsoft.com/zh-cn/library/System.DirectoryServices.DirectorySearcher_properties(v=vs.80).aspx

      用户密码的过期日期可以通过DirectoryEntry对象的InvokeGet方法获得,不过要加载一次DirectoryEntry的话,总觉得很浪费!

    using (DirectoryEntry resultDirectoryEntry = userResult.GetDirectoryEntry())
    {
        userInfo.PasswordExpirationDate = DateTime.Parse(resultDirectoryEntry.InvokeGet("PasswordExpirationDate").ToString());
    }

       所以我还是愿意自己算一下,用最近密码设置时间+系统设置的密码过期天数。最近密码设置时间对应“pwdLastSet”,如果用DirectoryEntry对象的Properties取,那是个“System.__ComObject”类型值,幸好SearchResult对象的“pwdLastSet”可以直接取为long,这个值是Windows文件时间,可以再转为本地时间。

long fileTime = (userResult.Properties["pwdLastSet"][0] as long?).GetValueOrDefault();
userInfo.PasswordLastSet = DateTime.FromFileTime(fileTime);

      系统密码过期天数是通过组策略设置的,可以在OU路径下通过“maxPwdAge”属性获取,SearchResult对象的“maxPwdAge”也可以直接取为long。

bubuko.com,布布扣
 directorySearcher.SearchScope = SearchScope.Base;
 directorySearcher.Filter = @"(objectClass=*)";
 directorySearcher.PropertiesToLoad.Add("maxPwdAge");
 SearchResult ouResult = directorySearcher.FindOne();
 long maxPwdAge = 0;
 if (ouResult.Properties.Contains("maxPwdAge"))
 {
      maxPwdAge = TimeSpan.FromTicks((long)ouResult.Properties["maxPwdAge"][0]).Days * -1;
 }
bubuko.com,布布扣

      最后,用户的密码过期就可以这么求了!

 userInfo.MaxPasswordAge = maxPwdAge;
 if (!userInfo.IsPasswordNeverExpire)
 {
      userInfo.PasswordExpirationDate = userInfo.PasswordLastSet.AddDays(userInfo.MaxPasswordAge);
 }

      查询用户信息OK,剩下贴段将用户信息导出Excel的代码:

bubuko.com,布布扣 View Code

      还有使用SmtpClient发送邮件的代码,可以自定义个HTML文件做模版内容:

bubuko.com,布布扣 View Code

 

     最后,将这些代码整合起来,就是检查用户密码过期的小工具了!由于笔者水平有限,文中难免会有些疏漏和错误,代码也有待不断优化,欢迎各位高手提出宝贵的建议!

参考资料:

DirectoryEntry 类使用 http://msdn.microsoft.com/zh-cn/library/z9cddzaa(v=vs.110).aspx

DirectorySearcher 类使用 http://msdn.microsoft.com/zh-cn/library/System.DirectoryServices.DirectorySearcher(v=vs.90).aspx

轻量目录访问协议 (LDAP) http://msdn.microsoft.com/en-us/library/aa367008(VS.85).aspx

检查密码将过期用户小工具下载地址:http://files.cnblogs.com/CSharpDevelopers/UserPasswordSetChecker.zip

AD帐户操作C#示例代码(二)——检查密码将过期的用户,布布扣,bubuko.com

AD帐户操作C#示例代码(二)——检查密码将过期的用户

标签:blog   http   使用   os   io   文件   数据   for   

原文地址:http://www.cnblogs.com/lschenblog/p/3897988.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!