我们正在编写一个允许用户通过Intranet上的Web应用程序更改其帐户密码的系统。
起初,一切似乎都在顺利进行。在开发过程中,我们的测试帐户的密码可以毫无问题地更改。
然而,当我们制作系统时,我们开始遇到问题。以下是症状:
以下是相关的代码片段:
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上的文档也没有。
答案 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
这会强制用户在更改为新密码之前知道先前的密码。
我希望这有帮助,