65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (7投票s)

2013年3月21日

CPOL

1分钟阅读

viewsIcon

52275

本文档描述了如何从 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 上方使用这些属性。将类逐字复制到您的项目中,仅更改命名空间,一切都会正常工作。祝你好运!

© . All rights reserved.