活动目录用户密码过期日期.NET/ OU 组策略

14
我搜索了该网站以获取有关信息,并找到了以下内容: ASP.NET C# Active Directory - 查看用户密码过期前的时间 其中解释了如何根据域策略获取密码过期的值。
我的问题是:如果用户拥有一个OU组策略,该策略具有不同的MaxPasswordAge值,覆盖了域组策略中指定的值,如何通过编程方式获取OU的组策略对象?
编辑:为使此问题更加清晰,我添加了此编辑。我想知道的是能否告诉用户密码何时过期。据我所知,该日期值可以由域本地策略或组对象策略控制。我有一个Linq2DirectoryService提供程序,可将Linq转换为Ldap查询。因此,使用LDAP查询获取日期过期值将对此有利。如果您的答案包括在此方程式中支持.NET的对象包装器,则会非常准确!

没有评论吗?关于组策略管理控制台,我的环境是Server 2003,有人对这个软件有专业知识吗?帮帮忙! - dexter
4个回答

15

让我从http://support.microsoft.com/kb/323750开始,此页面包含Visual Basic和VBScript示例,以及http://www.anitkb.com/2010/03/how-to-implement-active-directory.html介绍了maxPwdAge OU设置如何影响计算机而不是用户。它还有一条评论指向AloInfo.exe,这是一个可以用于获取密码年龄的来自Microsoft的工具。

以下是示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;

namespace LDAP
{
    class Program
    {
        static void Main(string[] args)
        {
            string domainAndUsername = string.Empty;
            string domain = string.Empty;
            string userName = string.Empty;
            string passWord = string.Empty;
            AuthenticationTypes at = AuthenticationTypes.Anonymous;
            StringBuilder sb = new StringBuilder();

            domain = @"LDAP://w.x.y.z";
            domainAndUsername = @"LDAP://w.x.y.z/cn=Lawrence E."+
                        " Smithmier\, Jr.,cn=Users,dc=corp,"+
                        "dc=productiveedge,dc=com";
            userName = "Administrator";
            passWord = "xxxpasswordxxx";
            at = AuthenticationTypes.Secure;

            DirectoryEntry entry = new DirectoryEntry(
                        domain, userName, passWord, at);

            DirectorySearcher mySearcher = new DirectorySearcher(entry);

            SearchResultCollection results;
            string filter = "maxPwdAge=*";
            mySearcher.Filter = filter;

            results = mySearcher.FindAll();
            long maxDays = 0;
            if(results.Count>=1)
            {
                Int64 maxPwdAge=(Int64)results[0].Properties["maxPwdAge"][0];
                maxDays = maxPwdAge/-864000000000;
            }

            DirectoryEntry entryUser = new DirectoryEntry(
                        domainAndUsername, userName, passWord, at);
            mySearcher = new DirectorySearcher(entryUser);

            results = mySearcher.FindAll();
            long daysLeft=0;
            if (results.Count >= 1)
            {
                var lastChanged = results[0].Properties["pwdLastSet"][0];
                daysLeft = maxDays - DateTime.Today.Subtract(
                        DateTime.FromFileTime((long)lastChanged)).Days;
            }
            Console.WriteLine(
                        String.Format("You must change your password within"+
                                      " {0} days"
                                     , daysLeft));
            Console.ReadLine();
        }
    }
}

@LarrySmithmier,你有别的方法来回答Dexter的问题吗? - FMFF
@FMFF 我没有做 PInvoke 版本。你需要比上面说的更多吗?我最近没有看过这个,现在可能有更简单/更好的方法来完成它。 - Larry Smithmier
1
@FMFF 请查看 https://dev59.com/22HVa4cB1Zd3GeqPpbIZ 并查看它是否适用于您。 - Larry Smithmier
@LarrySmithmier 有没有可能用户(在用户名中)没有查看maxPwdAge的权限(除了那些影响他的权限)? - a.farkas2508
我能够获取maxPwdAge。但是pwdLastSet抛出了“索引超出范围”的异常。我调试了代码并发现没有可用的pwdLastSet属性。 - soccer7
显示剩余4条评论

11

以下代码对我来说有效,可以获取域和本地用户账户的密码到期日期:

public static DateTime GetPasswordExpirationDate(string userId, string domainOrMachineName)
{
    using (var userEntry = new DirectoryEntry("WinNT://" + domainOrMachineName + '/' + userId + ",user"))
    {
        return (DateTime)userEntry.InvokeGet("PasswordExpirationDate");
    }
}

1
我收到了错误信息:目标调用引发了异常。 - Vikalp Kapadiya
@GerkeGeurts,“userId”通常是什么样子的?这和“samAccountName”是一样的吗? - styfle
userId 确实是 SAM 帐户名称。 - Gerke Geurts
是的,强制转换是安全的。我已经相应地更新了代码示例。 - Gerke Geurts
其他答案在服务器上失败了,但这个可以正常工作。 - Furkan Ekinci
显示剩余2条评论

4
使用以下方法获取账户的过期日期 -
public static DateTime GetPasswordExpirationDate(string userId)
    {
        string forestGc = String.Format("GC://{0}", Forest.GetCurrentForest().Name);
        var searcher = new DirectorySearcher();
        searcher = new DirectorySearcher(new DirectoryEntry(forestGc));
        searcher.Filter = "(sAMAccountName=" + userId + ")";
        var results = searcher.FindOne().GetDirectoryEntry();
        return (DateTime)results.InvokeGet("PasswordExpirationDate");
    }

1
微软建议不要使用DirectoryEntry.InvokeGet(请参见https://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.invokeget(v=vs.110).aspx)。我已经发布了一个替代方案。 - Tawab Wakil

1

之前的一些答案依赖于DirectoryEntry.InvokeGet方法,Microsoft表示不应该使用该方法。因此这里提供另一种方法:

public static DateTime GetPasswordExpirationDate(UserPrincipal user)
{
    DirectoryEntry deUser = (DirectoryEntry)user.GetUnderlyingObject();
    ActiveDs.IADsUser nativeDeUser = (ActiveDs.IADsUser)deUser.NativeObject;
    return nativeDeUser.PasswordExpirationDate;
}

您需要添加对ActiveDS COM库的引用,通常可以在C:\ Windows \ System32 \ activeds.tlb中找到。


实际上,这个解决方案与使用DirectoryEntry.InvokeGet("PasswordExpirationDate")完全相同。两者都调用ActiveDS COM接口。然而,它以一种更加复杂的方式实现了这一点! - Gerke Geurts
我不能确认或否认该观点。但是,我提供的链接权威地表示不应使用InvokeGet,而我没有找到关于COM接口类似明确的声明。 - Tawab Wakil
不建议使用InvokeGet,因为它绕过了在使用DirectoryEntry.Properties时有效的属性缓存。由于PasswordExpirationDate属性未存储在DirectoryEntry.Properties集合中,因此必须使用InvokeGet方法来访问它。 - Gerke Geurts
就InvokeGet的实现而言,请参见https://github.com/dotnet/corefx/blob/master/src/System.DirectoryServices/src/System/DirectoryServices/DirectoryEntry.cs#L832。 - Gerke Geurts
所有的观点都很好。那么使用我的答案的唯一原因可能是性能是最重要的考虑因素,或者您需要实现一些自定义行为。但这些原因应该很少见。 - Tawab Wakil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接