勾选“下次登录需改密码”时,如何用ldap3修改AD用户密码?
解决AD用户"下次登录必须改密码"时无法绑定修改密码的问题
当AD用户账号被勾选“下次登录必须更改密码”时,用户无法通过旧密码完成LDAP绑定(错误码773),导致无法用自身连接执行密码修改操作。以下是两种可行的解决方案:
方案1:使用具有权限的服务账号修改密码
这是最稳定的方案,核心思路是用一个拥有重置用户密码权限的AD服务账号先完成LDAP绑定,再通过该连接修改目标用户的密码。
操作步骤
在AD中创建一个服务账号,并为其赋予目标用户所在OU的“重置密码”权限:
- 打开AD用户和计算机,找到目标用户所在OU,右键→属性→安全→高级
- 添加服务账号,在权限列表中勾选“重置密码”,应用设置
修改代码如下(替换服务账号信息):
from ldap3 import ALL, Connection, Server from ldap3.core.exceptions import LDAPBindError, LDAPOperationResult # 解析UAC值的函数(避免使用内置函数名sum作为参数) def Calc(arr, sum_val): ds = sum_val res = [] tt = arr donee = True a = 0 while donee: a = 0 for i in tt: if i == ds: res.append(i) ds = 0 if i > ds: break a += 1 if ds == 0: donee = False break tt = tt[0:a] ds = ds - tt[a-1] if ds < 0: break res.append(tt[a-1]) return res uac_val = [1,2,8,16,32,64,128,256,512,2048,4096,8192,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,67108864] # LDAP服务器配置 ldsp_server = f"ldaps://192.168.16.130" root_dn = "dc=DOMAIN,dc=com" # 目标用户信息 ldap_user_name = 'user1' ldap_password = 'OldPassword' user_dn = f'cn={ldap_user_name},cn=Users,{root_dn}' # 服务账号信息(需拥有重置密码权限) service_user_dn = 'cn=ADServiceAccount,cn=Users,dc=DOMAIN,dc=com' service_user_pwd = 'ServiceAccountPassword' try: server = Server(ldsp_server, get_info=ALL, use_ssl=True) # 先尝试用用户自身凭证绑定 conn = Connection(server, user=user_dn, password=ldap_password) if not conn.bind(): print(conn.result) err = str(conn.result).split("data")[1].split(',')[0].strip() if err == "773": print(f"{ldap_user_name}的密码需在下次登录时修改") newpass = input("请输入新密码: ") # 用服务账号建立连接 service_conn = Connection(server, user=service_user_dn, password=service_user_pwd) if service_conn.bind(): # 执行密码修改 change_success = service_conn.extend.microsoft.modify_password(user_dn, newpass) if change_success: print("密码修改成功!") else: print("密码修改失败:", service_conn.result) service_conn.unbind() else: print("服务账号绑定失败,无法修改密码:", service_conn.result) else: print("绑定失败原因:", conn.result) conn.unbind() exit(0) else: print("成功绑定LDAP服务器") print(f"查询用户: DOMAIN/{ldap_user_name}") # 直接通过entry属性获取值,避免字符串分割的不可靠性 if conn.search(user_dn, '(objectClass=user)', attributes=['userAccountControl', 'pwdLastSet']): entry = conn.entries[0] print(entry) uac_value = entry.userAccountControl.value pwd_last_set = entry.pwdLastSet.value print("上次密码修改日期:", pwd_last_set) print("UAC值:", uac_value) uac_flags = Calc(uac_val, uac_value) if 65536 in uac_flags: print("该用户密码永不过期!") else: print("获取用户详情失败") conn.unbind() exit(0) except (LDAPBindError, LDAPOperationResult) as e: print("绑定失败错误:", conn.result) if 'conn' in locals(): conn.unbind() exit(0)
方案2:尝试用未绑定连接修改密码(兼容性有限)
部分AD环境允许用户在绑定失败返回773的情况下,直接使用未认证的连接执行密码修改操作。可以尝试修改原代码,去掉exit(0),直接在bind失败后调用修改方法,但这种方式不保证在所有AD环境下生效:
# 原代码中773分支修改为: if err == "773" : print(f"{ldap_user_name}的密码需在下次登录时修改") newpass = input("请输入新密码: ") # 直接使用当前未绑定的连接尝试修改 ii = conn.extend.microsoft.modify_password(user, newpass) print(ii) if ii: print("密码修改成功!") else: print("密码修改失败!") # 不要立即exit,后续可以尝试重新绑定
关键注意事项
- 原代码中使用
sum作为函数参数,这是Python内置函数名,容易引发冲突,建议改为sum_val - 原代码通过字符串分割解析用户属性的方式不可靠,直接使用
entry.属性名.value是更稳定的做法 - 服务账号的权限必须配置正确,否则无法完成密码修改操作
内容的提问来源于stack exchange,提问作者therealdanish
相关产品推荐
相关产品推荐

