如何从 Active Directory 获取真实的最后登录时间






4.67/5 (7投票s)
本文档描述了如何从 Active Directory 获取用户的真实最后登录时间,以及如何使用自定义 Active Directory 属性。
引言
本文档描述了如何从 Active Directory 获取用户的**真实**最后登录日期和时间,以及如何使用自定义 Active Directory 属性。
背景
.NET System.DirectoryServices.AccountManagement
类(来自 Framework 3.5)提供了一些便捷的功能,可以以相当简单的方式访问 Active Directory 用户。检索用户就像这样简单
using (var adContext = new PrincipalContext(ContextType.Domain, ADDomainName, ADContainer)
{
username = "ADusername";
var foundUser = UserPrincipal.FindByIdentity(adContext, username);
if (foundUser != null)
{
// do all sorts of neat stuff with user object
}
}
现在你可能会期望(就像任何其他正常人一样),从我们刚刚创建的 UserPrincipal
对象中获取 Active Directory 用户的最后登录时间也像这样简单
var lastlogonDateTime = founduser.LastLogon;
但是不幸的是,事实并非如此。经过一些研究,我发现以下内容:“由于 lastLogon
属性不会在域中复制,如果我们的新用户从未登录到域控制器 B,那么域控制器 B 将不知道用户的最后登录时间。”
无论如何,它都无法提供正确的最后登录时间。它根本不起作用,我们该如何解决?这让我头疼不已。
它存在,但被隐藏了。您必须使用扩展属性来检索正确的时间。通过扩展 Userprincipal
类,您可以访问这些属性。不仅可以访问真实的最后登录时间,还可以访问不同的属性,例如传真号码等。
使用代码
以下是您可以替代 UserPrincipal
使用的扩展 Userprincipal
类:
using System;
using System.DirectoryServices.AccountManagement;
using System.Reflection;
namespace MyNameSpace
{
/// <summary>
/// some properties must be retrieved by getting extended properties
/// this is unfortunately a protected method and only accessible
/// by using our own derived user from UserPrincipal
/// </summary>
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")]
public class UserPrincipalExtended : UserPrincipal
{
public UserPrincipalExtended(PrincipalContext context) : base(context) { }
public UserPrincipalExtended(PrincipalContext
context,
string samAccountName,
string password,
bool enabled)
: base(context, samAccountName, password, enabled) { }
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context,
typeof(UserPrincipalExtended),
identityValue);
}
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context,
IdentityType identityType,
string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context,
typeof(UserPrincipalExtended),
identityType,
identityValue);
}
#region custom attributes
[DirectoryProperty("RealLastLogon")]
public DateTime? RealLastLogon
{
get
{
if (ExtensionGet("LastLogon").Length > 0)
{
var lastLogonDate = ExtensionGet("LastLogon")[0];
var lastLogonDateType = lastLogonDate.GetType();
var highPart = (Int32)lastLogonDateType.InvokeMember("HighPart",
BindingFlags.GetProperty, null, lastLogonDate, null);
var lowPart = (Int32)lastLogonDateType.InvokeMember("LowPart",
BindingFlags.GetProperty | BindingFlags.Public, null, lastLogonDate, null);
var longDate = ((Int64)highPart << 32 | (UInt32)lowPart);
return longDate > 0 ? (DateTime?) DateTime.FromFileTime(longDate) : null;
}
return null;
}
}
#endregion
}
}
这将为您提供正确的 LastLogon 日期和时间。您还可以添加属性来检索其他(自定义)属性,以备将来使用:
[DirectoryProperty("HomePage")]
public string HomePage
{
get
{
if (ExtensionGet("HomePage").Length != 1)
return null;
return (string)ExtensionGet("HomePage")[0];
}
set { this.ExtensionSet("HomePage", value); }
}
这非常方便,因为原始 Userprincipal
类仅暴露了大约 10% 的 Active Directory 属性。
请记住在类 UserPrincipalExtended
上方使用这些属性。将类逐字复制到您的项目中,仅更改命名空间,一切都会正常工作。祝你好运!