IT知识库 购物 网址 游戏 小说 歌词 快照 开发 股票 美女 新闻 笑话 | 汉字 软件 日历 阅读 下载 图书馆 编程 China
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程 CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
站长资讯 .NET新手 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA VisualStudio ASP.NET-MVC .NET控件开发 EntityFramework WinRT-Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动 Html-Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP OracleERP DynamicsCRM K2 BPM 信息安全 企业信息 Android开发 iOS开发 WindowsPhone WindowsMobile 其他手机 敏捷开发 项目管理 软件工程 SQLServer Oracle MySQL NoSQL 其它数据库 Windows7 WindowsServer Linux
  IT知识库 -> .NET新手区 -> AD帐户操作C#示例代码(二)——检查密码将过期的用户 -> 正文阅读

[.NET新手区]AD帐户操作C#示例代码(二)——检查密码将过期的用户

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

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


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

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

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

     这次我们主要用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里,减少搜索结果的返回行数:

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

     Filter语法请参考:http://msdn.microsoft.com/en-us/library/aa746475.aspx、http://www.ldapexplorer.com/en/manual/109010000-ldap-filter-syntax.htm。
     userAccountControl标志请参考:http://support.microsoft.com/kb/305144/zh-cn、http://msdn.microsoft.com/zh-cn/library/ms680832(VS.85).aspx、http://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。

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

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

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

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


                   string connectionString = string.Format("Provider = Microsoft.ACE.OLEDB.12.0;Data Source ={0};Extended Properties='Excel 12.0 Xml;HDR=YES'", fileName);
                    using (OleDbConnection oleDbConnection = new OleDbConnection(connectionString))
                    {
                        oleDbConnection.Open();
                        using (OleDbCommand oleDbCommand = new OleDbCommand())
                        {
                            oleDbCommand.Connection = oleDbConnection;
                            //const string sqlCreateTable = @"CREATE TABLE [Sheet1$] ([登录名] TEXT,[显示名] TEXT,[邮箱] TEXT,[已禁用] TEXT,[密码永不过期] TEXT,[密码过期设置天数] TEXT,[密码最近设置时间] TEXT,[密码过期时间] TEXT,[剩余密码过期天数] TEXT)";
                            //oleDbCommand.CommandText = sqlCreateTable;
                            //oleDbCommand.ExecuteNonQuery();
                            foreach (var user in users)
                            {
                                oleDbCommand.CommandText = string.Format(@"INSERT INTO [Sheet1$]([登录名], [显示名], [邮箱],[已禁用], [密码永不过期], [密码过期设置天数],[密码最近设置时间],[密码过期时间],[剩余密码过期天数]) VALUES ('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}');", user.SamAccountName, user.Name, user.Mail, user.IsDisabled ? "" : "", user.IsPasswordNeverExpire ? "" : "", user.MaxPasswordAge.ToString(), user.PasswordLastSet.ToString(), user.PasswordExpirationDate.ToString(), user.SurplusPasswordExpirationDays.ToString());
                                oleDbCommand.ExecuteNonQuery();
                            }
                        }
                    }

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


            using (SmtpClient smtpClient = new SmtpClient())
            {
                if (!string.IsNullOrEmpty(mailMessageInfo.Host))
                {
                    smtpClient.Host = mailMessageInfo.Host;
                }
                if (!string.IsNullOrEmpty(mailMessageInfo.Port))
                {
                    smtpClient.Port = int.Parse(mailMessageInfo.Port);
                }
                smtpClient.Credentials = new System.Net.NetworkCredential();
                if (!string.IsNullOrEmpty(mailMessageInfo.UserName))
                {
                    NetworkCredential networkCredential = new NetworkCredential { UserName = mailMessageInfo.UserName };
                    if (!string.IsNullOrEmpty(mailMessageInfo.PassWord))
                    {
                        networkCredential.Password = mailMessageInfo.PassWord;
                    }
                    smtpClient.Credentials = networkCredential;
                }
                MailMessage mailMessage = new MailMessage();
                if (!string.IsNullOrEmpty(mailMessageInfo.From))
                {
                    mailMessage.From = new MailAddress(mailMessageInfo.From);
                }
                foreach (string to in mailMessageInfo.ToList)
                {
                    if (!string.IsNullOrWhiteSpace(to))
                    {
                        mailMessage.To.Add(to);
                    }
                }
                if (mailMessageInfo.CcList != null && mailMessageInfo.CcList.Length > 0)
                {
                    foreach (string cc in mailMessageInfo.CcList)
                    {
                        if (!string.IsNullOrWhiteSpace(cc))
                        {
                            mailMessage.To.Add(cc);
                        }
                    }
                }
                mailMessage.IsBodyHtml = true;
                string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "templates", mailMessageInfo.TemplateFileName);
                string body = File.ReadAllText(path);
                Regex regexImg = new Regex(@"<img\s[^>]*>", RegexOptions.IgnoreCase);
                Regex regexSrc = new Regex(
                    @"src=(?:(['""])(?<src>(?:(?!\1).)*)\1|(?<src>[^\s>]+))",
                    RegexOptions.IgnoreCase | RegexOptions.Singleline);
                MatchCollection matchCollection = regexImg.Matches(body);
                Dictionary<string, string> contentIds = new Dictionary<string, string>();
                foreach (Match matchImg in matchCollection)
                {
                    if (regexSrc.IsMatch(matchImg.Groups[0].Value))
                    {
                        Match matchSrc = regexSrc.Match(matchImg.Groups[0].Value);
                        string srcValue = matchSrc.Groups["src"].Value;
                        if (!srcValue.StartsWith("http:", System.StringComparison.OrdinalIgnoreCase)
                            && !srcValue.StartsWith("file:", System.StringComparison.OrdinalIgnoreCase))
                        {
                            if (srcValue.IndexOf("/") == 0)
                            {
                                srcValue = srcValue.Substring(1);
                            }
                            string attachmentContentId = Path.GetFileName(srcValue).Replace(".", string.Empty);
                            body = body.Replace(matchSrc.Groups["src"].Value, "cid:" + attachmentContentId);
                            if (!contentIds.ContainsKey(attachmentContentId))
                            {
                                string inlinePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "templates", srcValue.Replace(@"/", @"\"));
                                Attachment inline = new Attachment(inlinePath);
                                inline.ContentDisposition.Inline = true;
                                inline.ContentDisposition.DispositionType = DispositionTypeNames.Inline;
                                inline.ContentId = attachmentContentId;
                                if (srcValue.EndsWith("gif", StringComparison.OrdinalIgnoreCase))
                                {
                                    inline.ContentType.MediaType = MediaTypeNames.Image.Gif;
                                }
                                else
                                {
                                    inline.ContentType.MediaType = MediaTypeNames.Image.Jpeg;
                                }
                                inline.ContentType.Name = Path.GetFileName(inlinePath);
                                mailMessage.Attachments.Add(inline);
                                contentIds.Add(attachmentContentId, null);
                            }
                        }
                    }
                }
                mailMessage.Body = body; ;
                mailMessage.BodyEncoding = Encoding.UTF8;
                mailMessage.Subject = mailMessageInfo.Subject;
                mailMessage.SubjectEncoding = Encoding.UTF8;
                smtpClient.Send(mailMessage);
            }

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
上一篇文章      下一篇文章      查看所有文章
加:2015-03-29 20:35:22  更:2017-05-14 05:28:19 
 
  .NET新手区 最新文章
将ZIP文件添加到程序集资源文件然后在运行时
Web服务的调用
.NET创建WebService服务简单的例子
多线程编程学习笔记——任务并行库(三)
序列化和反序列化
Spring学习之路
多线程(2)Thread
五小步让VS Code支持AngularJS智能提示
Log4net入门(SQL篇)
C#十五子游戏
技术频道: 站长资讯 .NET新手区 ASP.NET C# WinForm Silverlight WCF CLR WPF XNA Visual Studio ASP.NET MVC .NET控件开发 Entity Framework WinRT/Metro Java C++ PHP Delphi Python Ruby C语言 Erlang Go Swift Scala R语言 Verilog 其它语言 架构设计 面向对象 设计模式 领域驱动设计 Html/Css JavaScript jQuery HTML5 SharePoint GIS技术 SAP Oracle ERP Dynamics CRM K2 BPM 信息安全 企业信息化其他 Android开发 iOS开发 Windows Phone Windows Mobile 其他手机开发 敏捷开发 项目与团队管理 软件工程其他 SQL Server Oracle MySQL NoSQL 其它数据库 Windows 7 Windows Server Linux
脚本语言: vbs/VBScript DOS/BAT hta htc python perl 游戏相关 VBA 远程脚本 ColdFusion ruby专题 autoit seraphzone PowerShell linux shell Lua Golang Erlang 其它教程
网站开发: CSS/HTML/Xhtml html5 CSS XML/XSLT Dreamweaver教程 经验交流 开发者乐园 Android开发资料
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 新闻资讯 小游戏 Chinese Culture 股票 三丰软件 开发 中国文化 网文精选 阅读网 看图 日历 万年历 2019年6日历
2019-6-25 0:25:00
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT知识库