13

私たちは、ユーザーがイントラネット上の Web アプリケーションを介してアカウントのパスワードを変更できるようにするシステムを作成しています。

最初は、すべてが順調に進んでいるように見えました。開発中、テスト アカウントのパスワードは問題なく変更できました。

しかし、システムを稼働させたとき、問題が発生し始めました。症状は次のとおりです。

  1. 最初は、すべて問題ありません。ユーザーはパスワードを変更できます。
  2. ある時点で、UserPrincipal.FindByIdentity で次のエラーが発生します:「System.Runtime.InteropServices.COMException: 認証メカニズムが不明です。」
  3. それ以降、Web アプリケーションを介してパスワードを変更しようとすると、「System.Runtime.InteropServices.COMException: サーバーが動作していません。」というエラーが発生します。
  4. アプリ プールを手動でリサイクルすると、さらにエラーが発生し始めるまで、すべてが自動的に修正されるように見えます。つまり、プロセスはフェーズ 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 のドキュメントもそうではありません。

4

1 に答える 1

11

最初に気付くのは、 Principalから継承されたAuthenticablePrincipalから継承されたUserPrincipal.FindByIdentityを使用していることです。クラスには既知のメモリリークがあるため、これをすべて言います. このMSDN エントリを見ると、一番下にMicrosoft のGary Caldwellが次のように述べていることがわかります。PrincipalFindByIdentity

基礎となる実装は DirectorySearcher と SearchResultsCollection を使用しますが、ドキュメントで説明されているように SearchResultsCollection で dispose を呼び出さないため、この呼び出しにはアンマネージ メモリ リークがあります。

これはあなたの問題だと思います。アプリケーション プールがリセットされてメモリが破棄されるまで、メモリ リークが原因でアプリケーション プールがいっぱいになり、最終的にエラーが発生します。

Active Directory 機能を使用する場合、以下を使用してユーザーのパスワードを設定します。

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

これにより、ユーザーは新しいパスワードに変更する前に以前のパスワードを知る必要があります。

これが役に立てば幸いです。

ありがとう!

于 2009-12-10T23:28:52.573 に答える