简体   繁体   English

如何使用 C# .NET 跨域设置/更改 Active Directory 用户密码?

[英]How to set/change Active Directory user password across domains using C# .NET?

I have been searching around for quite some time now how to set/change a password and revoke/restore a user but have yet to find a solution that actually works for me.我一直在寻找如何设置/更改密码和撤销/恢复用户已经有一段时间了,但还没有找到真正适合我的解决方案。

I am beginning to lean towards the fact that I am crossing domains as the problem, even though I can programmatically create/delete/update and even connect/disconnect users from groups.我开始倾向于将跨域作为问题的事实,尽管我可以以编程方式创建/删除/更新甚至连接/断开用户与组的连接。

Basically, I've tried the following ways:基本上,我尝试了以下方法:

DirectoryEntry account = new DirectoryEntry("LDAP://" + adHostname + "/" + dn, adUserName, adPassword);

account.Invoke("SetPassword", "Password1");
account.Properties["LockOutTime"].Value = 0;
account.CommitChanges();

And also并且

account.Invoke("SetPassword", new object[] { "Password1" });

They both ultimately throw the error "One or more input parameters are invalid\\r\\n"他们最终都抛出错误“一个或多个输入参数无效\\r\\n”

I then have tried to use the .NET 3.5 approach using principal context.然后我尝试使用使用主体上下文的 .NET 3.5 方法。

using (var context = new PrincipalContext(ContextType.Domain, adHostname, myContainer, ContextOptions.SimpleBind, adUserName, adPassword))
    {
        using (var user = UserPrincipal.FindByIdentity(context, account.Properties["sAMAccountName"].Value.ToString()))
        {
             user.SetPassword(password);
        }
    }    

This approach is also throwing the same error as above.这种方法也会抛出与上面相同的错误。 If I switch some things around (I can't seem to remember all the combinations I've tried), it will sometimes throw a "Local error has occurred" COM Exception.如果我切换一些东西(我似乎无法记住我尝试过的所有组合),它有时会抛出“发生本地错误”的 COM 异常。

Any help is much appreciated.任何帮助深表感谢。


## EDIT WITH WORKING SOLUTION ## ##使用工作解决方案编辑##

using System.DirectoryServices.Protocols;

LdapDirectoryIdentifier identifier = new LdapDirectoryIdentifier(_adHostname, 636);
NetworkCredential credential = new NetworkCredential(_adUserName, _adPassword);

string password = "MyRandomComplexPassword";


using (LdapConnection connection = new LdapConnection(identifier, credential))
{
    connection.SessionOptions.SecureSocketLayer = true;
    connection.SessionOptions.VerifyServerCertificate += delegate { return true; };
    connection.AuthType = AuthType.Basic;
    connection.Bind(credential);

    DirectoryAttributeModification modPwd = new DirectoryAttributeModification();
    modPwd.Operation = DirectoryAttributeOperation.Replace;
    modPwd.Name = "unicodePwd";
    modPwd.Add(Encoding.Unicode.GetBytes("\"" + password + "\""));

    DirectoryAttributeModification[] dMods = new DirectoryAttributeModification[1];
    dMods[0] = modPwd;

    ModifyRequest modReq = new ModifyRequest(accountDN, dMods);

    DirectoryResponse pwdModResponse;
    pwdModResponse = connection.SendRequest(modReq);    
}

"new DirectoryEntry" does not bind the user account. “new DirectoryEntry”不绑定用户帐户。 The user needs to be searched out for setting password.需要搜索用户设置密码。 Like this:像这样:

DirectoryEntry account = new DirectoryEntry("LDAP://" + adHostname + "/" + dn, null, null, AuthenticationTypes.Secure | AuthenticationTypes.Sealing | AuthenticationTypes.Signing);

DirectorySearcher search = new DirectorySearcher(account);
search.Filter = "(&(objectClass=user)(sAMAccountName=" + adUserName + "))";
account = search.FindOne().GetDirectoryEntry();

account.Invoke("SetPassword", "Password1");
account.Properties["LockOutTime"].Value = 0;
account.CommitChanges();

This one always works for me.这个总是对我有用。 Hope it helps.希望能帮助到你。 First add reference to System.DirectoryServices.AccountManagement首先添加对System.DirectoryServices.AccountManagement引用

    string hostName = "myDomain";
    string adminName = "admin";
    string adminPassword = "password";

    public static void ResetPassword(string username, string password)
            {
        using (PrincipalContext pContext = new PrincipalContext(ContextType.Domain, hostName, adminName, adminPassword))
                                {
                                    UserPrincipal up = UserPrincipal.FindByIdentity(pContext, username);
                                    if (up != null)
                                    {
                                        up.SetPassword(password);
                                        up.Save();
                                    }
                                }
                        }

You can also do all sort of things to UserPrincipal like lock account, expire password, get last logon, etc.您还可以对 UserPrincipal 执行各种操作,例如锁定帐户、过期密码、获取上次登录等。

See this article: https://www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#7见这篇文章: https : //www.codeproject.com/Articles/18102/Howto-Almost-Everything-In-Active-Directory-via-C#7

You'll notice in all the samples that we're binding directly to the directoryEntry and not specifying a server or credentials.您会在所有示例中注意到我们直接绑定到 directoryEntry 而没有指定服务器或凭据。 If you do not want to use an impersonation class you can send credentials directly into the DirectoryEntry constructor.如果您不想使用模拟类,您可以将凭据直接发送到 DirectoryEntry 构造函数中。 The impersonation class is helpful for those times when you want to use a static method and don't want to go through the trouble of creating a DirectoryContext object to hold these details.当您想要使用静态方法并且不想经历创建 DirectoryContext 对象来保存这些详细信息的麻烦时,模拟类很有用。 Likewise you may want to target a specific domain controller.同样,您可能希望针对特定的域控制器。

Target Specific Domain Controllers or Credentials目标特定域控制器或凭据

Everywhere in the code that you see: LDAP:// you can replace with LDAP://MyDomainControllerNameOrIpAddress as well as everywhere you see a DirectoryEntry class being constructed you can send in specific credentials as well.在您看到的代码中的任何地方: LDAP:// 您可以替换为 LDAP://MyDomainControllerNameOrIpAddress 以及您看到正在构造的 DirectoryEntry 类的任何地方,您也可以发送特定凭据。 This is especially helpful if you need to work on an Active Directory for which your machine is not a member of it's forest or domain or you want to target a DC to make the changes to.如果您需要在您的计算机不是其林或域成员的 Active Directory 上工作,或者您希望以 DC 为目标进行更改,这将特别有用。

//Rename an object and specify the domain controller and credentials directly //重命名对象并直接指定域控制器和凭据

public static void Rename(string server,
    string userName, string password, string objectDn, string newName)
{
    DirectoryEntry child = new DirectoryEntry("LDAP://" + server + "/" + 
        objectDn, userName, password);
    child.Rename("CN=" + newName);
}

Good afternoon!下午好! I have read your question several times but I definitely do not understand the essence of your problem ...我已经多次阅读您的问题,但我绝对不明白您的问题的本质......

If you need to change your password, first read the materials: https://docs.microsoft.com/en-us/windows/win32/api/iads/nf-iads-iadsuser-setpassword https://docs.microsoft.com/ru-ru/office/client-developer/outlook/mapi/hresult And keep in mind that when you change your password, your account (on behalf of which you will attempt to change your password) must have sufficient administrative privileges in the Active Directory domain to change your password!如需更改密码,请先阅读资料: https : //docs.microsoft.com/en-us/windows/win32/api/iads/nf-iads-iadsuser-setpassword https://docs.microsoft。 com/ru-ru/office/client-developer/outlook/mapi/hresult请记住,当您更改密码时,您的帐户(您将代表该帐户尝试更改密码)必须在Active Directory 域来更改您的密码! It is especially important to note that the new password must meet the requirements set by your domain's policies!尤其需要注意的是,新密码必须符合您的域策略设置的要求!

        string distinguishedName = $"LDAP://CN=SuperUser,DC=company,DC=local";
        if (!DirectoryEntry.Exists(distinguishedName))
            return;
        DirectoryEntry deEntry = new DirectoryEntry(distinguishedName);
        try
        {
            deEntry.Invoke("SetPassword", "MyNew@#123Pass12091");
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
        Console.Read();

EDIT - added after the comments编辑 - 在评论后添加

And so too lazy to help ungrateful users, additionally the cons are also clicked ... For you specially on the test stand put an AD domain and a client not connected to the domain for a clean check!所以懒得帮助忘恩负义的用户,另外缺点也被点击了......专门为您在测试台上放置一个AD域和一个未连接到域的客户端进行干净检查!

static void Main(string[] args)
    {
        Console.WriteLine($"Client - {Environment.UserDomainName}\\{Environment.MachineName}");
        try
        {
            DirectoryEntry dirEntrForeignDomain = new DirectoryEntry();
            dirEntrForeignDomain.AuthenticationType = AuthenticationTypes.ServerBind;
            dirEntrForeignDomain.Path = $"LDAP://172.28.145.73/DC=kul,DC=local";
            dirEntrForeignDomain.Username = $"Администратор@kul.local";
            dirEntrForeignDomain.Password = $"Qwerty123";

            SetPasswordUser(dirEntrForeignDomain); // but it is better to use OFFICIAL - AuthenticablePrincipal.SetPassword(String)
            ResetPasswordUser(dirEntrForeignDomain);
            UnResetPasswordUser(dirEntrForeignDomain);

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
        Console.Read();
    }

    static public void SetPasswordUser(DirectoryEntry de)
    {
        using (DirectorySearcher dirSearchUser = new DirectorySearcher(de))
        {
            dirSearchUser.Filter = "(&(objectClass=user)(sAMAccountName=test))";
            SearchResult searchResUser = dirSearchUser.FindOne();
            if (searchResUser != null)
            {
                DirectoryEntry dirEntryFoundUser = searchResUser.GetDirectoryEntry();
                dirEntryFoundUser.Invoke("SetPassword", new object[] { "Qwerty!!!" });
                dirEntryFoundUser.CommitChanges();
                Console.WriteLine("Set password is - Qwerty!!!");
            }
        }
    }
    static public void ResetPasswordUser(DirectoryEntry de)
    {
        using (DirectorySearcher dirSearchUser = new DirectorySearcher(de))
        {
            dirSearchUser.Filter = "(&(objectClass=user)(sAMAccountName=test))";
            SearchResult searchResUser = dirSearchUser.FindOne();
            if (searchResUser != null)
            {
                DirectoryEntry dirEntryFoundUser = searchResUser.GetDirectoryEntry();
                dirEntryFoundUser.Properties["pwdLastSet"].Value = 0;
                dirEntryFoundUser.CommitChanges();
                Console.WriteLine("ResetPasswordUser!");
            }
        }
    }
    static public void UnResetPasswordUser(DirectoryEntry de)
    {
        using (DirectorySearcher dirSearchUser = new DirectorySearcher(de))
        {
            dirSearchUser.Filter = "(&(objectClass=user)(sAMAccountName=test))";
            SearchResult searchResUser = dirSearchUser.FindOne();
            if (searchResUser != null)
            {
                DirectoryEntry dirEntryFoundUser = searchResUser.GetDirectoryEntry();
                dirEntryFoundUser.Properties["pwdLastSet"].Value = -1;
                dirEntryFoundUser.CommitChanges();
                Console.WriteLine("UnResetPasswordUser ...");
            }
        }
    }
  • Set a password for a user from another domain为来自另一个域的用户设置密码
  • For a user in a different domain set a requirement to change the password对于不同域中的用户设置更改密码的要求
  • For a user in a different domain unSet a requirement to change the password对于不同域中的用户取消设置更改密码的要求

Logs日志

Client - DESKTOP-KUL\\DESKTOP-KUL客户 - DESKTOP-KUL\\DESKTOP-KUL
Set password is - Qwerty!!!设置密码是 - Qwerty!!!
ResetPasswordUser!重置密码用户!
UnResetPasswordUser ... UnResetPasswordUser ...

It is very strange that you failed with "SetPassword" via "UserPrincipal"...很奇怪,你通过“UserPrincipal”失败了“SetPassword”......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM