我一直在网上寻找解决这个问题的日子。
我正在开展一个项目,我要求用户允许用户使用ASP.NET Web应用程序更改密码。
我必须使用“ChangePassword”而不是“SetPassword”,因为我必须强制执行密码历史记录,并且不允许LDS中的任何用户拥有超出他们需要的任何权限。我试图在开发环境中完成此任务。我有两台机器“Server1”(LDS,PingFederate,CA)和“Server2”(IIS)。我以为我可能会遇到问题,因为我没有在两个盒子之间设置SSL,所以我昨天花了半天时间设置CA并为两台机器创建证书。我相当肯定它正在工作,因为我不再在错误日志中看到任何错误,并且我可以使用LDP使用端口636登录到LDS并检查SSL。我还应该提到这些机器不在域环境中。我已在测试网络上的所有计算机上编辑了hosts文件。
我尝试了不同的代码变体:
public static bool ChangePassword(string email, string pwd, string newPwd)
{
DirectoryEntry user = GetCN(email);
string username = user.Properties["cn"][0].ToString();
DirectoryEntry de = new DirectoryEntry();
de.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer;
de.Path = user.Path;
de.Username = username;
de.Password = pwd;
try
{
Object obj = de.NativeObject;
de.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingSsl;
de.Options.PasswordPort = Convert.ToInt32(ConfigurationManager.AppSettings["LDAPPort_ExternalUsers"]);
de.Invoke("ChangePassword", new object[] { pwd, newPwd });
de.CommitChanges();
return true;
}
catch (Exception ex)
{
return false;
}
}
public static bool ChangePassword(string email, string pwd, string newPwd)
{
DirectoryEntry user = GetCN(email);
string username = user.Properties["cn"][0].ToString();
DirectoryEntry de = new DirectoryEntry(user.Path, username, pwd, AuthenticationTypes.Secure | AuthenticationTypes.SecureSocketsLayer);
try
{
de.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingSsl;
de.Options.PasswordPort = Convert.ToInt32(ConfigurationManager.AppSettings["LDAPPort_ExternalUsers"]);
de.Invoke("ChangePassword", new object[] { pwd, newPwd });
return true;
}
catch (Exception ex)
{
return false;
}
}
public static bool ChangePassword(string email, string pwd, string newPwd)
{
DirectoryEntry userInc = GetCN(email);
string username = userInc.Properties["cn"][0].ToString();
using (DirectoryEntry searchRoot = new DirectoryEntry(ConfigurationManager.AppSettings["LDAPConnectionString_ExternalUsers"], username, pwd, AuthenticationTypes.SecureSocketsLayer | AuthenticationTypes.Secure))
using (DirectorySearcher ds = new DirectorySearcher(searchRoot))
{
ds.Filter = "(|(objectCategory=user)(cn=" + username + "))";
SearchResult sr = ds.FindOne();
if (sr != null)
{
using (DirectoryEntry user = sr.GetDirectoryEntry())
{
user.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingClear;
user.Options.PasswordPort = Convert.ToInt32(ConfigurationManager.AppSettings["LDAPPort_ExternalUsers"]);
//user.Invoke("SetOption", new object[] { 6, Convert.ToInt32(ConfigurationManager.AppSettings["LDAPPort_ExternalUsers"]) });
//user.Invoke("SetOption", new object[] { 7, 1 });
user.Invoke("ChangePassword", new object[] { pwd, newPwd });
}
return true;
}
}
return false;
}
我在Object obj = de.NativeObject;的第一个版本上得到一个例外。我正在使用它来确定绑定是否正确发生,并作为调试步骤插入,因为这是我通过端口389对用户进行身份验证的方式。例外是“登录失败:未知用户名或密码错误。”
我在de.Options.PasswordEncoding = PasswordEncodingMethod.PasswordEncodingSsl的第二个版本上获得了一个例外;例外是“登录失败:未知用户名或密码错误。”
我在SearchResult的第三个版本上获得了一个例外sr = ds.FindOne();例外是“登录失败:未知用户名或密码错误。”
如果我尝试使用AuthenticatioTypes.None |在端口389下运行此代码AuthenticationTypes.FastBind,它将在de.Invoke失败(“ChangePassword”,new object [] {pwd,newPwd});除了“未知名称”。我真的希望在SSL下运行它,或者至少不清楚地传输任何密码。我的网站是通过HTTPS运行的。我确实尝试修改LDS上的dsHeuristics值,以便我可以通过非SSL连接进行密码更改,但这也不起作用。
任何人可能会提出任何建议都会非常感激。
答案 0 :(得分:0)
我终于可以让密码更改工作了。我使用了http://www.informit.com/articles/article.aspx?p=474649&seqNum=4
上提供的信息我使用了这三个功能:
private static DirectoryConnection GetConnection(string server, NetworkCredential credential, bool useSsl)
{
LdapConnection connection =
new LdapConnection(server);
if (useSsl)
{
connection.SessionOptions.SecureSocketLayer = true;
}
else
{
connection.SessionOptions.Sealing = true;
}
connection.Bind(credential);
return connection;
}
private static void ChangePassword(DirectoryConnection connection, string userDN, string oldPassword, string newPassword)
{
DirectoryAttributeModification deleteMod = new DirectoryAttributeModification();
deleteMod.Name = "unicodePwd";
deleteMod.Add(GetPasswordData(oldPassword));
deleteMod.Operation= DirectoryAttributeOperation.Delete;
DirectoryAttributeModification addMod = new DirectoryAttributeModification();
addMod.Name = "unicodePwd";
addMod.Add(GetPasswordData(newPassword));
addMod.Operation = DirectoryAttributeOperation.Add;
ModifyRequest request = new ModifyRequest(userDN, deleteMod, addMod);
DirectoryResponse response = connection.SendRequest(request);
}
private static byte[] GetPasswordData(string password)
{
string formattedPassword;
formattedPassword = String.Format("\"{0}\"", password);
return (Encoding.Unicode.GetBytes(formattedPassword));
}
我从允许更改密码的页面后面的代码中调用了这样的函数:
NetworkCredential credential = new NetworkCredential("[user with access to the LDS", "pwd");
DirectoryConnection connection;
try
{
//Setup our connection
connection = ADAuth.GetConnection(ConfigurationManager.AppSettings["LDAPServer_ExternalUsers"] + ":" + ConfigurationManager.AppSettings["LDAPPort_ExternalUsers"], credential, true);
//Attempt to change the password
ADAuth.ChangePassword(connection, ADAuth.GetDN(userID.Text).Properties["distinguishedName"].Value.ToString(), currPass.Text, newPass.Text);
//Send success message to user
ErrorLit.Text = "<p>Password change successful!</p>";
//Dispose the connection
IDisposable disposable = connection as IDisposable;
if (disposable != null)
disposable.Dispose();
}
catch (Exception ex)
{
//There was an error, tell the user
errors += "<li>Error changing password</li>";
ErrorLit.Text = errors + "</ul>";
return;
}