System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity的奇怪问题

时间:2009-12-10 18:50:38

标签: memory-leaks active-directory ldap directoryservices account-management

我们正在编写一个允许用户通过Intranet上的Web应用程序更改其帐户密码的系统。

起初,一切似乎都在顺利进行。在开发过程中,我们的测试帐户的密码可以毫无问题地更改。

然而,当我们制作系统时,我们开始遇到问题。以下是症状:

  1. 起初,一切都很好。用户 可以更改密码。
  2. 有些人 点,发生以下错误 UserPrincipal.FindByIdentity: “System.Runtime.InteropServices.COMException: 身份验证机制是 未知。 “
  3. 从此,尝试 通过网络更改密码 应用程序导致错误: “System.Runtime.InteropServices.COMException: 服务器无法运行。 “
  4. 如果我手动回收应用池,     一切似乎都在解决     更多错误开始发生...即,     这个过程从头再次开始     第1阶段。
  5. 以下是相关的代码片段:


        private static PrincipalContext CreateManagementContext() {
            return new PrincipalContext(
                ContextType.Domain, 
                ActiveDirectoryDomain, 
                ActiveDirectoryManagementAccountName,
                ActiveDirectoryManagementAccountPassword);
        }
    
    
        private static void ChangeActiveDirectoryPasword(string username, string password) {
            if (username == null) throw new ArgumentNullException("username");
            if (password == null) throw new ArgumentNullException("password");
    
            using (var context = CreateManagementContext())
            using (var user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username)) {
                user.SetPassword(password);
            }
        }
    

    有关为何发生这种情况的任何线索?谷歌搜索没有提供任何真正有用的信息,MSDN上的文档也没有。

1 个答案:

答案 0 :(得分:11)

我注意到的第一件事是您使用的UserPrincipal.FindByIdentity继承自AuthenticablePrincipal继承的Principal。我说这一切都是因为Principal类在FindByIdentity中有一个已知的内存泄漏。如果您查看此MSDN entry,您会在底部注意到Microsoft的Gary Caldwell表示以下内容:

  

此调用具有非托管内存泄漏   因为潜在的实施   使用DirectorySearcher和   SearchResultsCollection但没有   呼叫处理   SearchResultsCollection为   文件描述。

我猜这是你的问题。内存泄漏导致应用程序池填满并最终导致错误,直到重置应用程序池并释放内存。

当我们使用任何活动目录功能时,我们使用以下内容来完成用户密码的设置:

Public Shared Function GetUserAccount(ByVal username As String) As DirectoryEntry
    Dim rootPath As String = GetRootPath()
    Using objRootEntry As New DirectoryEntry(rootPath)
        Using objAdSearcher As New DirectorySearcher(objRootEntry)
            objAdSearcher.Filter = "(&(objectClass=user)(samAccountName=" & username & "))"
            Dim objResult As SearchResult = objAdSearcher.FindOne()
            If objResult IsNot Nothing Then Return objResult.GetDirectoryEntry()
        End Using
    End Using
    Return Nothing
End Function

Public Shared Sub SetPassword(ByVal username As String, ByVal newPassword As String)
    Using objUser As DirectoryEntry = GetUserAccount(username)
        If objUser Is Nothing Then Throw New UserNotFoundException(username)
        Try
            objUser.Invoke("SetPassword", newPassword)
            objUser.CommitChanges()
        Catch ex As Exception
            Throw New Exception("Could not change password for " & username & ".", ex)
        End Try
    End Using
End Sub

此外,如果您希望用户直接更改密码并且您不想依赖他们的诚实,您可能需要考虑使用LDAP的ChangePassword功能:

Public Shared Sub ChangePassword(ByVal username As String, ByVal oldPassword As String, ByVal newPassword As String)
    Using objUser As DirectoryEntry = GetUserAccount(username)
        If objUser Is Nothing Then Throw New UserNotFoundException(username)
        Try
            objUser.Invoke("ChangePassword", oldPassword, newPassword)
            objUser.CommitChanges()
        Catch ex As TargetInvocationException
            Throw New Exception("Could not change password for " & username & ".", ex)
        End Try
    End Using
End Sub

这会强制用户在更改为新密码之前知道先前的密码。

我希望这有帮助,

谢谢!