Подтвердить что ты не робот

Странная проблема с System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity

Мы пишем систему, которая позволяет пользователю изменять свой пароль учетной записи через веб-приложение в нашей интрасети.

Сначала все казалось плавным. Во время разработки пароли для наших тестовых учетных записей могли быть изменены без проблем.

Однако, когда мы запустили систему, мы начали сталкиваться с проблемами. Вот симптомы:

  • Сначала все в порядке. пользователей могут менять свои пароли.
  • На некоторых то возникает следующая ошибка в UserPrincipal.FindByIdentity: "System.Runtime.InteropServices.COMException: Механизм аутентификации неизвестно. "
  • С этого момента, пытаясь сменить пароль через Интернет приложение приводит к ошибке: "System.Runtime.InteropServices.COMException: Сервер не работает. "
  • Если я вручную переработаю пул приложений,   все, кажется, исправляется до тех пор, пока   больше ошибок начинают происходить... т.е.   процесс начинается снова и снова   фаза 1.

Вот соответствующий фрагмент кода:


    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);
        }
    }

Любые подсказки относительно того, почему это происходит? Поиск в Google не вызывает ничего полезного, и ни один из них не является документом в MSDN.

4b9b3361

Ответ 1

Прежде всего я замечаю, что вы используете UserPrincipal.FindByIdentity, который унаследован от AuthenticablePrincipal, который наследуется от Principal. Я говорю все это, потому что класс Principal имеет известную утечку памяти в FindByIdentity. Если вы посмотрите на запись MSDN, вы заметите внизу, что Гэри Колдуэлл от Microsoft сказал следующее:

Этот вызов имеет неуправляемую утечку памяти потому что базовое воплощение использует DirectorySearcher и SearchResultsCollection, но не вызывать на SearchResultsCollection как документ описывает.

Я бы предположил, что это твоя проблема. Утечка памяти заставляет пул приложений заполнять и, наконец, вызывать ошибки, пока пул приложений не будет reset и память будет удалена.

Когда мы используем любые активные функции каталогов, для выполнения настройки пароля пользователя мы используем следующее:

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

Кроме того, если вы хотите, чтобы пользователи напрямую меняли пароли, и вы не хотите полагаться на их честность, вам может потребоваться использовать функцию ChangePassword для LDAP следующим образом:

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

Это заставляет пользователя знать предыдущий пароль перед тем, как перейти на новый.

Я надеюсь, что это поможет,

Спасибо!