如何在WinForms的ListView中展示Active Directory用户的lastLogon属性
问题原因
- 空引用报错:
lastLogon是AD可选属性,从未登录过域的用户该属性值为null,直接对null调用ToString()会触发空引用异常。同时lastLogon在AD中以Windows文件时间格式的长整型存储,直接转字符串也无法得到可读的时间格式。 - 拿到的时间不是真实最后登录时间:AD中
lastLogon属性不会在多域控制器之间同步,每个域控制器仅存储用户在本节点的最后登录时间;你之前用到的lastLogonTimestamp虽然是多DC同步属性,但默认存在9~14天的同步延迟,仅用于判断长期未登录用户,不能作为实时登录时间依据。直接调用UserPrincipal.LastLogon仅会查询当前连接的单个DC记录,若用户最近在其他DC登录,拿到的就不是最新值。
解决方案
单域控制器环境(最简方案)
如果你的域只有一个域控制器,直接对可空属性做判空和时间格式化处理即可,修改你循环内的赋值逻辑:
foreach (UserPrincipal u in results) { string[] arr = new string[4]; arr[0] = u.GivenName ?? ""; arr[1] = u.SamAccountName ?? ""; // 对可空时间类型判空,无值时显示自定义提示 arr[2] = u.LastLogon.HasValue ? u.LastLogon.Value.ToString("yyyy-MM-dd HH:mm:ss") : "从未登录"; arr[3] = u.LastPasswordSet.HasValue ? u.LastPasswordSet.Value.ToString("yyyy-MM-dd HH:mm:ss") : "无记录"; lstActiveDirectory.Items.Add(new ListViewItem(arr)); }
多域控制器环境(取最新登录时间)
如果域内有多个域控制器,需要遍历所有DC查询用户的登录时间,取最大值作为真实最后登录时间,完整调整代码如下:
if (rdbUsers.Checked == true) { string strADPath = txtLDAP.Text.ToString(); string strUsername = txtUsername.Text.ToString(); string strPassword = txtPassword.Text.ToString(); try { using (PrincipalContext context = new PrincipalContext(ContextType.Domain, strADPath)) { if (context.ValidateCredentials(strUsername, strPassword)) { try { lstActiveDirectory.Clear(); lstActiveDirectory.Sorting = SortOrder.Ascending; lstActiveDirectory.Columns.Add("First Name", 150); lstActiveDirectory.Columns.Add("Last Name", 150); lstActiveDirectory.Columns.Add("Last Logon", 180); lstActiveDirectory.Columns.Add("Last Password Reset", 180); // 获取当前域所有域控制器列表 Domain domain = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, strADPath, strUsername, strPassword)); DomainControllerCollection dcs = domain.DomainControllers; PrincipalContext ctx = new PrincipalContext(ContextType.Domain, strADPath, strUsername, strPassword); UserPrincipal usr = new UserPrincipal(ctx); PrincipalSearcher ps = new PrincipalSearcher(usr); PrincipalSearchResult<Principal> results = ps.FindAll(); foreach (UserPrincipal u in results) { DateTime? latestLogon = null; // 遍历所有DC查询用户的最后登录时间,取最大值 foreach (DomainController dc in dcs) { using (PrincipalContext dcCtx = new PrincipalContext(ContextType.Domain, dc.Name, strUsername, strPassword)) { UserPrincipal dcUser = UserPrincipal.FindByIdentity(dcCtx, IdentityType.SamAccountName, u.SamAccountName); if (dcUser != null && dcUser.LastLogon.HasValue) { if (!latestLogon.HasValue || dcUser.LastLogon.Value > latestLogon.Value) { latestLogon = dcUser.LastLogon.Value; } } } } string[] arr = new string[4]; arr[0] = u.GivenName ?? ""; arr[1] = u.SamAccountName ?? ""; arr[2] = latestLogon.HasValue ? latestLogon.Value.ToString("yyyy-MM-dd HH:mm:ss") : "从未登录"; arr[3] = u.LastPasswordSet.HasValue ? u.LastPasswordSet.Value.ToString("yyyy-MM-dd HH:mm:ss") : "无记录"; lstActiveDirectory.Items.Add(new ListViewItem(arr)); } } catch (Exception ex) { MessageBox.Show("查询用户信息失败:" + ex.Message); rdbUsers.Checked = false; lstActiveDirectory.Clear(); } } else { MessageBox.Show("服务器、用户名或密码错误"); } } } catch (Exception ex) { MessageBox.Show(ex.Message); rdbUsers.Checked = false; lstActiveDirectory.Clear(); } }
注意:多DC遍历查询会增加接口耗时,用户量大的场景建议增加加载提示优化体验。
内容的提问来源于stack exchange,提问作者Allrounder
相关产品推荐
相关产品推荐

