【问题标题】:Active Directory - check if password never expires?Active Directory - 检查密码是否永不过期?
【发布时间】:2011-08-30 16:59:15
【问题描述】:

在 Visual Basic 中有没有办法检查用户的密码是否在 Active Directory 中设置为永不过期?

我找到了找到上次更改日期的方法,但找不到其他可用选项。

Dim de As DirectoryServices.DirectoryEntry = GetUser(uDetails.username)
Dim objUser = GetObject(de.Path)
If objUser.PasswordLastChanged < DateTime.Now.AddMonths(-3) Then
...

在哪里可以找到所有可用objUser 属性的列表?

【问题讨论】:

    标签: asp.net vb.net active-directory .net-2.0


    【解决方案1】:

    如果您使用的是 .NET 3.5 及更高版本,则应查看 System.DirectoryServices.AccountManagement (S.DS.AM) 命名空间。在此处阅读所有相关信息:

    基本上,您可以定义域上下文并在 AD 中轻松找到用户和/或组:

    // set up domain context
    PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
    
    // find a user
    UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
    
    if(user != null)
    {
       // one of the properties exposed is "PasswordNeverExpires" (bool)
       if(user.PasswordNeverExpires)
       { 
          // do something here....      
          ...   
       }
    }
    

    新的 S.DS.AM 让在 AD 中与用户和组一起玩变得非常容易!

    【讨论】:

      【解决方案2】:

      对于.NET 2.0,您可以使用一些LDAP。神奇的部分是userAccountControl:1.2.840.113556.1.4.803:=65536。第一部分是您要搜索的属性,第二部分表示“按位与”,第三部分是要检查的按位标志,在本例中是第 17 位。您可以在 How to query Active Directory by using a bitwise filter 中查看有关 Active Directory 中的按位 AND 和 OR 的更多信息。

      在下面的代码中,使用您的 domain controller (DC) 和 FQDN 更新 SearchRoot 变量。

      Imports System.DirectoryServices
      
      Public Class Form1
      
          Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
              ''//Bind to the root of our domain
              ''//    YOU_DOMAIN_CONTROLLER should be one of your DCs
              ''//    EXAMPLE and COM are the parts of your FQDN
              Dim SearchRoot As DirectoryEntry = New DirectoryEntry("LDAP://YOUR_DOMAIN_CONTROLLER/dc=EXAMPLE,dc=COM")
      
              ''//Create a searcher bound to the root
              Dim Searcher As DirectorySearcher = New DirectorySearcher(SearchRoot)
      
              ''//Set our filer. The last part is dumb but that is the way that LDAP was built.
              ''//It basically does a bitwise AND looking for the 17th bit to be set on that property "userAccountControl" which is the "password never expires" bit
              ''//See this if you care to learn more http://support.microsoft.com/kb/269181
              Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
              ''//Find all of the results
      
              Dim Results = Searcher.FindAll()
              Dim DE As DirectoryEntry
      
              ''//Loop through each result
              For Each R As SearchResult In Results
      
                  ''//Get the result as a DirectoryEntry object
                  DE = R.GetDirectoryEntry()
      
                  ''//Output the object name
                  Console.WriteLine(DE.Name)
              Next
          End Sub
      
      End Class
      

      【讨论】:

        【解决方案3】:
        public bool isPasswordExpired(String p_UserName, String p_DomainName)
        {
            bool m_Check=false;
        
            int m_Val1 = (int)de1.Properties["userAccountControl"].Value;
            int m_Val2 = (int) 0x10000;
        
            if (Convert.ToBoolean(m_Val1 & m_Val2))
            {
                m_Check = true;
            } //end
        
            return m_Check
        }
        

        【讨论】:

          【解决方案4】:

          您可以使用以下来自 here 的代码,我从 C# 翻译并根据您的问题进行了一些修改(添加了一个 getter):

          Dim pwdNeverExpires = getPasswordNeverExpires("Tim")
          setPasswordNeverExpires("Tim", True)
          
          ' See http://msdn.microsoft.com/en-us/library/aa772300(VS.85).aspx
          <Flags()> _
          Private Enum ADS_USER_FLAG_ENUM
              ADS_UF_SCRIPT = 1
              ' 0x1
              ADS_UF_ACCOUNTDISABLE = 2
              ' 0x2
              ADS_UF_HOMEDIR_REQUIRED = 8
              ' 0x8
              ADS_UF_LOCKOUT = 16
              ' 0x10
              ADS_UF_PASSWD_NOTREQD = 32
              ' 0x20
              ADS_UF_PASSWD_CANT_CHANGE = 64
              ' 0x40
              ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128
              ' 0x80
              ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256
              ' 0x100
              ADS_UF_NORMAL_ACCOUNT = 512
              ' 0x200
              ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048
              ' 0x800
              ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096
              ' 0x1000
              ADS_UF_SERVER_TRUST_ACCOUNT = 8192
              ' 0x2000
              ADS_UF_DONT_EXPIRE_PASSWD = 65536
              ' 0x10000
              ADS_UF_MNS_LOGON_ACCOUNT = 131072
              ' 0x20000
              ADS_UF_SMARTCARD_REQUIRED = 262144
              ' 0x40000
              ADS_UF_TRUSTED_FOR_DELEGATION = 524288
              ' 0x80000
              ADS_UF_NOT_DELEGATED = 1048576
              ' 0x100000
              ADS_UF_USE_DES_KEY_ONLY = 2097152
              ' 0x200000
              ADS_UF_DONT_REQUIRE_PREAUTH = 4194304
              ' 0x400000
              ADS_UF_PASSWORD_EXPIRED = 8388608
              ' 0x800000
              ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216
              ' 0x1000000
          End Enum
          
          Protected Overridable Function getPasswordNeverExpires(ByVal userName As String) As Boolean
              Const userNameString As String = "userName"
              Const userFlagsString As String = "userFlags"
          
              Dim machineName As String = Environment.MachineName
          
              Dim userInThisComputerDirectoryEntry As DirectoryEntry = getUserInThisComputerDirectoryEntry(machineName, userName)
              If userInThisComputerDirectoryEntry Is Nothing Then
                  Throw New ArgumentException("not found in " & machineName, userNameString)
              End If
          
              Dim userFlagsProperties As PropertyValueCollection = userInThisComputerDirectoryEntry.Properties(userFlagsString)
              Dim userFlags As ADS_USER_FLAG_ENUM = CType(userFlagsProperties.Value, ADS_USER_FLAG_ENUM)
          
              Return userFlags = (userFlags Or ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD)
          End Function
          
          Protected Overridable Sub setPasswordNeverExpires(ByVal userName As String, ByVal passwordNeverExpires As Boolean)
              Const userNameString As String = "userName"
              Const userFlagsString As String = "userFlags"
          
              Dim machineName As String = Environment.MachineName
          
              Dim userInThisComputerDirectoryEntry As DirectoryEntry = getUserInThisComputerDirectoryEntry(machineName, userName)
              If userInThisComputerDirectoryEntry Is Nothing Then
                  Throw New ArgumentException("not found in " & machineName, userNameString)
              End If
          
              Dim userFlagsProperties As PropertyValueCollection = userInThisComputerDirectoryEntry.Properties(userFlagsString)
          
              Dim userFlags As ADS_USER_FLAG_ENUM = CType(userFlagsProperties.Value, ADS_USER_FLAG_ENUM)
              Dim newUserFlags As ADS_USER_FLAG_ENUM = userFlags
          
              If passwordNeverExpires Then
                  newUserFlags = newUserFlags Or ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD
              Else
                  newUserFlags = newUserFlags And (Not ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD)
              End If
          
              userFlagsProperties.Value = newUserFlags
          
              userInThisComputerDirectoryEntry.CommitChanges()
          End Sub
          
          Protected Overridable Function getUserInThisComputerDirectoryEntry(ByVal machineName As String, ByVal userName As String) As DirectoryEntry
              Dim computerDirectoryEntry As DirectoryEntry = getComputerDirectoryEntry(machineName)
          
              Const userSchemaClassName As String = "user"
              Return computerDirectoryEntry.Children.Find(userName, userSchemaClassName)
          End Function
          
          Protected Overridable Function getComputerDirectoryEntry(ByVal machineName As String) As DirectoryEntry
              'Initiate DirectoryEntry Class To Connect Through WINNT Protocol
              ' see: http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
              Const pathUsingWinNTComputerMask As String = "WinNT://{0},computer"
              Dim path As String = String.Format(pathUsingWinNTComputerMask, machineName)
              Dim thisComputerDirectoryEntry As New DirectoryEntry(path)
              Return thisComputerDirectoryEntry
          End Function
          

          您必须添加对System.DirectoryServices 的引用。我已经在没有Active Directory 的情况下使用 .NET Framework 4(它也应该在 2.0 下工作)在 Windows Server 2008 上对其进行了测试。但是您自己检查一下,并随意扩展它以获取/设置其他属性或连接到其他机器(SomeDomain/OtherComputerName 而不是Environment.MachineName)。

          【讨论】:

          • 咬人!我已更改服务器上的此设置以测试上述方法。这就是为什么今天有些任务无法以错误值 2147943730 开始的原因,因为我需要更新我的密码^^
          猜你喜欢
          • 1970-01-01
          • 2010-11-26
          • 2013-04-24
          • 2010-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-24
          相关资源
          最近更新 更多