我已经尝试了很多其他解决方案,但我仍然没有让它工作。有人可以帮我吗? 我的代码是这样的: 我看到了一些关于secureString的东西,我试图使用它,但它仍然不起作用。 我也看到了另一种解决方案,它说在变量中使用 var 而不是字符串。没用 我不知道我是否做错了什么,或者那些不起作用的解决方案。
public bool RedefinirSenha(string pUsuario, string pSenhaAtual, string pNovaSenha)
{
var NovaSenha = pNovaSenha;
var SenhaAtual = pSenhaAtual;
var Usuario = pUsuario;
//string Pwd = String.Format(@"""{0}""", NovaSenha);
//byte[] pwdCerto = System.Text.Encoding.Unicode.GetBytes(Pwd);
try
{
string LDAP = myLDAPpath;
DirectoryEntry ADcon = new DirectoryEntry(LDAP, Usuario, SenhaAtual, AuthenticationTypes.Secure);
if (ADcon != null)
{
DirectorySearcher search = new DirectorySearcher(ADcon);
search.Filter = "(SAMAccountName=" + Usuario + ")";
SearchResult result = search.FindOne();
if (result != null)
{
DirectoryEntry userEntry = result.GetDirectoryEntry();
if (userEntry != null)
{
try
{
userEntry.Invoke("ChangePassword", new object[] { SenhaAtual, NovaSenha }, AuthenticationTypes.Secure);
userEntry.Properties["LockOutTime"].Value = 0;
userEntry.CommitChanges();
userEntry.Close();
return true;
}
catch (Exception INex)
{
this.Erro = INex.Message + "COD:rn" + INex.InnerException;
userEntry.Close();
return false;
}
}
}
}
return true;
}
catch (Exception ex)
{
this.Erro = ex.Message;
return false;
}
}
首先,如果将变量声明为var
或string
,则在运行时没有区别。使用var
关键字可以让编译器决定类型是什么。因为您要为其分配string
,所以它也是一个字符串。在大多数情况下,var
就可以了。只有在极少数情况下,才需要显式指定类型。
其次,DirectoryEntry.Invoke
定义如下:
public object Invoke (string methodName, params object[] args);
这似乎需要传递一个object
数组,但事实并非如此。params
关键字是一种允许您将方法内部使用的多个参数作为数组传递的方法。所以当你这样称呼它时:
userEntry.Invoke("ChangePassword", new object[] { SenhaAtual, NovaSenha }, AuthenticationTypes.Secure);
第一个参数是object
数组,第二个参数是AuthenticationTypes.Secure
,然后这两个参数都放在args
数组中以便在Invoke
方法中使用。但这不是ChangePassword
想要的。如果这对您没有意义,请阅读params
关键字的文档,它应该会有所帮助。
当您调用.Invoke("ChangePassword", ...)
时,它将调用本机 WindowsIADsUser.ChangePassword
方法。这需要两个参数:一个是使用旧密码的string
,另一个是使用新密码的string
- 而不是object
数组和AuthenticationTypes
值。所以你应该这样称呼它:
userEntry.Invoke("ChangePassword", SenhaAtual, NovaSenha);
您无需担心身份验证,因为密码只能通过安全连接更改。在文档中,它说它的行为方式与 (IADsUser.SetPassword
](https://learn.microsoft.com/en-ca/windows/win32/api/iads/nf-iads-iadsuser-setpassword( 相同,它尝试了几种不同的方法来为您实现安全连接。
如果DirectoryEntry
连接已通过安全连接,则还有另一种方法可以更改密码。安全连接可以使用 Kerberos,这可以使用AuthenticationTypes.Sealing
来完成(如果您与域控制器位于同一网络上,则最好这样做(:
var ADcon = new DirectoryEntry(LDAP, Usuario, SenhaAtual, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
或者,如果使用 LDAPS(SSL 上的 LDAP(,只需在 LDAP 路径中指定端口 636 即可使用(如果您与域控制器不在同一网络上,这是唯一的方法(:
var ADcon = new DirectoryEntry("LDAP://example.com:636", Usuario, SenhaAtual);
如果这样做,那么您可以通过直接更新unicodePwd
属性来更改密码,以它想要的非常具体的方式(用引号括起来并以 UTF-16 编码(,如下所示:
userEntry.Properties["unicodePwd"].Remove(Encoding.Unicode.GetBytes($""{SenhaAtual}""));
userEntry.Properties["unicodePwd"].Add(Encoding.Unicode.GetBytes($""{NovaSenha}""));
这应该执行得稍微快一些,因为所有工作(更改密码和设置lockOutTime
(都是通过一个网络请求而不是两个网络请求完成的。