欢迎各位兄弟 发布技术文章
这里的技术是共享的
我自己亲自做的
function _readExcelFileSaveAdUsers($file)
{
global $user;
$diqu = _my_node_jses_terminal_manager_diqu();
$filepath = $file->uri;
require("sites/all/libraries/PHPExcel/Classes/PHPExcel/IOFactory.php");
// $filepath = iconv('UTF-8', 'GBK', $filepath);
// Check prerequisites
if (!file_exists($filepath)) {
exit("not found $filepath \n");
}
// $filepath = "sites/default/files/upload_assets/test_ok2-----ok.xlsx";
$filepath = str_replace('public://upload_assets', 'sites/default/files/upload_assets', $filepath);
$reader = PHPExcel_IOFactory::createReaderForFile($filepath); //设置以Excel5格式(Excel97-2003工作簿)
$PHPExcel = $reader->load($filepath); // 载入excel文件
$sheet = $PHPExcel->getActiveSheet(); //读取第一个工作表
$highestRow = $sheet->getHighestRow(); // 取得总行数
$headA1 = _removeCnEnSpace($sheet->getCell('A1')->getValue());
$headB1 = _removeCnEnSpace($sheet->getCell('B1')->getValue());
$headC1 = _removeCnEnSpace($sheet->getCell('C1')->getValue());
$headD1 = _removeCnEnSpace($sheet->getCell('D1')->getValue());
$headE1 = _removeCnEnSpace($sheet->getCell('E1')->getValue());
$headF1 = _removeCnEnSpace($sheet->getCell('F1')->getValue());
// var_dump($headD1);exit;
if (
$headA1 != '姓名' || $headB1 != '工号' || $headC1 != '邮箱'
|| $headD1 != '初始密码' || $headE1 != '仲裁主管工号'
|| $headF1 != '组织路径'
) {
drupal_set_message('是不是格式或文件头部有错误,请看示例', 'error');
return;
// $redirect = 'assets_save';
// return $redirect;
}
/** 循环读取每个单元格的数据 */
$dataset = array();
for ($row = 2; $row <= $highestRow; $row++) { //行数是以第1行开始
if(empty(_removeCnEnSpace($sheet->getCell('A' . $row)->getValue()))){
continue;
}
if(empty(_removeCnEnSpace($sheet->getCell('B' . $row)->getValue()))){
continue;
}
$dataset[$row - 2]['xingming'] = _removeCnEnSpace($sheet->getCell('A' . $row)->getValue());
$dataset[$row - 2]['gh'] = _removeCnEnSpace($sheet->getCell('B' . $row)->getValue());
$dataset[$row - 2]['mail'] = _removeCnEnSpace($sheet->getCell('C' . $row)->getValue());
$dataset[$row - 2]['password'] = _removeCnEnSpace($sheet->getCell('D' . $row)->getValue());
$dataset[$row - 2]['manager_gh'] = _removeCnEnSpace($sheet->getCell('E' . $row)->getValue());
//如果全称是 全称: CN=牛丽倩(15405430),OU=cccccccc-site,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn ,那么 zhuzhipath 就是 OU=cccccccc-site,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn
$dataset[$row - 2]['zhuzhipath'] = _removeCnEnSpace($sheet->getCell('F' . $row)->getValue());
empty($dataset[$row - 2]['zhuzhipath']) && $dataset[$row - 2]['zhuzhipath'] = "OU=cccccccc-site,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn";
}
// 获取当前时间加 60 天的 Unix 时间戳
$expireTimestamp = strtotime('+60 days');
// 将 Unix 时间戳转换为 Windows 文件时间
$windowsEpoch = 11644473600; // Unix 时间戳(1601-01-01 00:00:00 UTC)
$accountExpires = ($expireTimestamp + $windowsEpoch) * 10000000;
foreach ($dataset as $key => $value) {
$dataset[$key]['ad_person_dn'] = "CN=".$value['xingming']."(".$value['gh'].")".",".$value['zhuzhipath'];
$password = $value['password'];
$display_name = str_replace('.',' ',substr($value['mail'],0,strrpos($value['mail'],'@')))."(".$value['xingming'].")";
$dataset[$key]['ad_user_property'] = [
"objectClass" => array("top", "person", "organizationalPerson", "user"),
"samAccountName" => $value['gh'], // SAM 帐户名
"userPrincipalName" => $value['gh'].'@aaaaaaaa.com.cn', // 用户主体名称
"mail" => $value['mail'], // 用户主体名称
"unicodePwd" => iconv("UTF-8", "UTF-16LE", '"'.$password.'"'), // 密码必须是 UTF-16LE 编码
"givenName" => $value['xingming'], // 名字
"sn" => $value['xingming'], // 姓氏
"displayName" => $display_name, // 显示名称
"accountExpires" => $accountExpires, // 帐户60天永不过期
"userAccountControl" => "512", // 启用帐户
// "cn" => $value['xingming'], // 通用名称,,,为什么有了cn,会报错 Invalid DN syntax ,,,00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of
];
}
foreach($dataset as $key=>$value)
{
$bool = _create_ad_user($value['ad_person_dn'], $value['ad_user_property'],$diqu);
if($bool){
drupal_set_message($value['xingming'].' '.$value['gh'].' '.'创建成功');
$manager_ad_info_parter = _get_ad_info_from_ad($value['manager_gh'], array('dn','displayname'), $diqu);
$ad_manager_dn = $manager_ad_info_parter['dn'];//主管帐号的 dn
if($value['gh'] == $value['manager_gh']){
drupal_set_message("自己帐号 {$value['gh']} 的主管不能是自己 ",'warning');
continue;
}
$bool = _my_ldap_replace_ad_manager($value['ad_person_dn'],$ad_manager_dn,$diqu);
if($bool){
}else{
drupal_set_message("自己帐号 {$value['gh']} 的审核人改为 {$value['manager_gh']} 失败",'error');
}
}else{
drupal_set_message($value['xingming'].' '.$value['gh'].' '.'创建失败');
}
}
return true;
}
//创建 AD 用户
function _create_ad_user($ad_person_dn, $ad_user_property = array('*'), $diqu = 'js')
{
$ad_connect_infos = _my_get_ad_connect_infos();
$diqu = _my_node_jses_terminal_manager_diqu();
$ad_user = $ad_connect_infos[$diqu]['ad_user']; //域用户名 包含域名
$ad_pwd = $ad_connect_infos[$diqu]['ad_pwd'];
$host_ldaps = $ad_connect_infos[$diqu]['host_ldaps'];
// $conn = ldap_connect($host, $port); // 不加密的389 端口 不要写成ldap_connect($host.':'.$port)的形式
$conn = ldap_connect($host_ldaps); // 加密安全的 636 要写成ldap_connect($host.':'.$port)的形式
if ($conn)
{
//设置参数
ldap_set_option($conn, LDAP_OPT_DEBUG_LEVEL, 7);
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); //声明使用版本3
ldap_set_option($conn, LDAP_OPT_REFERRALS, 0); // Binding to ldap server
ldap_set_option($conn, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
$bd = ldap_bind($conn, $ad_user, $ad_pwd);
if ($bd)
{
//这里 dn 就是 distinguishedname ,就是尊贵的与别人可以区分开来的长名字
$status = ldap_add($conn, $ad_person_dn, $ad_user_property);
if ($status)
{
$infos = true;
}
else
{
// var_dump($ad_person_dn);
// var_dump($ad_user_property);
// var_dump(ldap_error($conn));
//
// var_dump(ldap_errno($conn));
// ldap_get_option($conn, LDAP_OPT_ERROR_STRING, $extendedError);
// var_dump($extendedError);
$infos = false;
}
}
else
{
$infos = false;
// drupal_set_message('修LDAP 绑定失败', 'error');
}
}
else
{
$infos = false;
// drupal_set_message('LDAP 连接失败', 'warning');
}
ldap_close($conn);
return $infos;
}
function _get_ad_info_from_ad($gh = '', $ad_user_property = array('*'), $diqu = 'js')
{
$ad_connect_infos = _my_get_ad_connect_infos();
$ad_user = $ad_connect_infos[$diqu]['ad_user']; //域用户名 包含域名
$ad_pwd = $ad_connect_infos[$diqu]['ad_pwd'];
$host = $ad_connect_infos[$diqu]['host'];
$port = $ad_connect_infos[$diqu]['port'];//一般都是389
$conn = ldap_connect($host);
// $conn = ldap_connect($host, $port); //不要写成ldap_connect($host.':'.$port)的形式
if ($conn) {
//设置参数
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); //声明使用版本3
ldap_set_option($conn, LDAP_OPT_REFERRALS, 0); // Binding to ldap server
$bd = ldap_bind($conn, $ad_user, $ad_pwd);
$basedn = $ad_connect_infos[$diqu]['basedn'];
if ($_GET['q'] == 'query_ad_info_ext' && $diqu = 'js') {//这是一种范围更大的 basedn
$basedn = 'dc=aaaaaaaa,dc=com,dc=cn';
}
else if($_GET['q'] == 'checking_ad_pwd' && $diqu = 'js'){//检查密码的时候,也检查所有的 范围更大的 basedn 吧
$basedn = 'dc=aaaaaaaa,dc=com,dc=cn';
}
// $filter = "(objectClass=*)";//选择器
// $filter = "(&(sAMAccountName=史平忠))";//选择器
$filter = "SamAccountName=" . $gh; //根据工号 比如 16666739
//$filter="(|(sn=史平忠*)(givenname=史平忠*))";
// $justthese = array('*'); //选择要获取的用户属性
$justthese = $ad_user_property; //选择要获取的用户属性
//$sr=ldap_search($conn, $basedn,$filter );
$sr = ldap_search($conn, $basedn, $filter, $justthese);
$infos = ldap_get_entries($conn, $sr);
$ad_info = $infos[0];
if ($bd) {
//drupal_set_message('LDAP 连接成功'); //相当于登录成功
} else {
$ad_info = false;
drupal_set_message('LDAP 连接失败', 'warning');
}
} else {
$ad_info = false;
drupal_set_message('无法连接到AD域服务器', 'error');
}
ldap_close($conn);
if (empty($ad_info)) {//这就是为了昆山的与嘉善的工号相同时进行的处理
if ($diqu == 'ks' && strlen($gh) == 9 && _startWith($gh, "C")) {
return _get_ad_info_from_ad(_make_ks_gh_reduce_C($gh), $ad_user_property, $diqu);
}
}
return $ad_info;
}
//给用户换领导
//这里 $ad_person_dn 就是 普通用户的 distinguishedname ,就是尊贵的与别人可以区分开来的长名字
//这里 $ad_manager_dn 就是 领导的 distinguishedname ,就是尊贵的与别人可以区分开来的长名字
function _my_ldap_replace_ad_manager($ad_person_dn, $ad_manager_dn, $diqu = 'js')
{
$ad_connect_infos = _my_get_ad_connect_infos();
$diqu = _my_node_jses_terminal_manager_diqu();
$ad_user = $ad_connect_infos[$diqu]['ad_user']; //域用户名 包含域名
$ad_pwd = $ad_connect_infos[$diqu]['ad_pwd'];
$host = $ad_connect_infos[$diqu]['host'];
$port = $ad_connect_infos[$diqu]['port'];//一般都是389
$conn = ldap_connect($host, $port); //不要写成ldap_connect($host.':'.$port)的形式
if ($conn)
{
//设置参数
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3); //声明使用版本3
ldap_set_option($conn, LDAP_OPT_REFERRALS, 0); // Binding to ldap server
$bd = ldap_bind($conn, $ad_user, $ad_pwd);
if ($bd)
{
//drupal_set_message('LDAP 连接成功'); //相当于登录成功
//这里 dn 就是 distinguishedname ,就是尊贵的与别人可以区分开来的长名字
$status = ldap_mod_replace($conn, $ad_person_dn, array('manager' => $ad_manager_dn));
if ($status)
{
$infos = true;
}
else
{
$infos = false;
}
}
else
{
$infos = false;
// drupal_set_message('修LDAP 绑定失败', 'error');
}
}
else
{
$infos = false;
// drupal_set_message('LDAP 连接失败', 'warning');
}
ldap_close($conn);
return $infos;
}
php ldap 创建 ad 帐户
要使用 PHP 和 LDAP 创建 Active Directory (AD) 帐户,你需要连接到 AD 服务器,使用具有适当权限的凭据进行绑定,然后使用所需的属性添加用户帐户 13。
以下是分步指南:
准备工作:
确保你有一个 PHP 环境 1。
确保 PHP LDAP 扩展已安装并启用 13。
拥有对 Active Directory 服务器的访问权限 1。
安装 LDAP 扩展(如果尚未安装):
对于 Debian/Ubuntu: sudo apt-get install php-ldap
3
对于 CentOS/RHEL: sudo yum install php-ldap
3
安装后重启你的 Web 服务器以启用该扩展 3。
建立 LDAP 连接:
$ldap_host = "ldap://your-ad-server.com";
$ldap_port = 389; # 因为是创建,是增加或修改数据 不要用不加密的389,要用 ldaps,即 端口是加密的 636
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("无法连接到 LDAP 服务器。");
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
使用 ldap_connect()
连接到 AD 服务器 13。
指定 AD 服务器地址和端口 13。对于安全连接,使用 LDAPS (ldaps://your-ad-server.com
) 13。
使用 ldap_set_option()
设置 LDAP 协议版本 2。
绑定到 LDAP 服务器:
$ldap_user = "cn=admin,dc=your,dc=com";
$ldap_pass = "admin-password";
$ldap_bind = ldap_bind($ldap_conn, $ldap_user, $ldap_pass);
if (!$ldap_bind) {
die("LDAP 绑定失败...");
}
使用 ldap_bind()
以及具有足够权限在指定的 OU(组织单元)中创建用户帐户的帐户的 DN(专有名称)和密码 13。
准备用户属性:
$user_data = array(
"objectClass" => array("top", "person", "organizationalPerson", "user"),
"samaccountname" => "newuser",
"userPrincipalName" => "newuser@yourdomain.com",
"unicodePwd" => iconv("UTF-8", "UTF-16LE", "P@ssword123"), // 密码必须是 UTF-16LE 编码
"givenName" => "New",
"sn" => "User",
"displayName" => "New User"
);
重要提示: unicodePwd
属性需要采用 UTF-16LE 编码 1。
默认情况下,该帐户将被禁用。要启用它,请将 userAccountControl
属性设置为 512
1。要创建一个启用的帐户,请将 userAccountControl
设置为 512
。
定义一个数组,其中包含新用户帐户的属性。必需的属性通常包括 objectClass
、samaccountname
、userPrincipalName
和 unicodePwd
13。
添加新用户帐户:
$dn = "cn=New User,ou=YourOU,dc=your,dc=com"; // 调整 OU
$result = ldap_add($ldap_conn, $dn, $user_data);
if ($result) {
echo "用户创建成功";
} else {
echo "创建用户失败: " . ldap_error($ldap_conn);
}
使用 ldap_add()
在指定的 OU 中创建新的用户帐户。新用户的 DN 应该遵循 cn=New User,ou=YourOU,dc=your,dc=com
格式。
关闭 LDAP 连接:
ldap_close($ldap_conn);
重要注意事项:
安全性: 使用 LDAPS 加密 LDAP 连接 13。
错误处理: 实施适当的错误处理以捕获并记录该过程中的任何问题 3。
密码复杂度: 确保密码符合你的 Active Directory 的复杂度要求 1。
权限: 用于绑定的帐户需要具有在目标 OU 中创建用户帐户的相应权限 13。
编码: 密切注意字符编码,尤其是密码 1。
通过执行这些步骤,你可以使用 PHP 和 LDAP 协议创建 AD 用户帐户 13。 请记住安全地处理敏感信息并实施适当的错误处理 13。
把 "unicodePwd" 拿掉 可以正常创建有户,但是 如果有 "unicodePwd" 会报错 Server is unwilling to perform 0000001F: SvcErr: DSID-031A126A, problem 5003 (WILL_NOT_PERFORM), data 0
当你在 PHP LDAP 创建脚本中包含 "unicodePwd"
属性时,出现
Server is unwilling to perform 0000001F: SvcErr: DSID-031A126A, problem 5003 (WILL_NOT_PERFORM), data 0
错误,这表明在用户创建期间设置密码时存在问题 1。 这通常与密码复杂度要求或用于设置密码的方法有关 13。
以下是潜在原因和解决方案的细分:
密码复杂度策略: Active Directory 强制执行密码复杂度策略。 如果提供的密码不符合这些要求(长度、字符类型等),服务器将拒绝该操作 1。
不正确的 unicodePwd
格式: unicodePwd
属性需要特定的格式 - 用引号括起来的 UTF-16LE 编码 1。
"unicodePwd" => iconv("UTF-8", "UTF-16LE", '"P@ssword123"') ; # 切记,密码是这样设置的
解决方案: 确保密码已正确转换为 UTF-16LE 并用引号括起来 1:
权限不足: 你用于绑定到 LDAP 服务器的帐户可能没有设置新用户帐户密码的必要权限 1。
解决方案: 使用具有足够权限的帐户(例如域管理员帐户)进行 LDAP 绑定操作 1。
SSL/TLS 要求: 修改 unicodePwd
属性通常需要安全连接 (LDAPS) 5。
解决方案: 确保你使用 LDAPS(端口 636)而不是 LDAP(端口 389)连接到 LDAP 服务器 5。
过早设置 userAccountControl
: 尝试在设置密码 同时 将 userAccountControl
属性设置为启用帐户有时会导致问题 47。
这是一个演示创建用户 不 初始设置密码或启用帐户的示例,然后在以后修改帐户以设置密码并启用它:
<?php
// LDAP 连接详情
$ldap_host = "ldap://your-ad-server.com";
$ldap_port = 389;
$ldap_dn = "cn=admin,dc=your,dc=com";
$ldap_password = "admin-password";
$base_dn = "dc=your,dc=com";
$user_ou = "ou=users," . $base_dn;
// 新用户详情
$username = "testuser";
$password = "P@ssword123";
$display_name = "Test User";
$user_principal_name = $username . "@yourdomain.com";
// 连接到 LDAP
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("无法连接到 LDAP 服务器。");
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);
// 绑定到 LDAP
$ldap_bind = ldap_bind($ldap_conn, $ldap_dn, $ldap_password);
if (!$ldap_bind) {
die("LDAP 绑定失败: " . ldap_error($ldap_conn));
}
// 1. 创建用户帐户(禁用,无密码)
$user_dn = "cn=" . $display_name . "," . $user_ou;
$user_data = array(
"objectClass" => array("top", "person", "organizationalPerson", "user"),
"samaccountname" => $username,
"userPrincipalName" => $user_principal_name,
"givenName" => "Test",
"sn" => "User",
"displayName" => $display_name
);
$result = ldap_add($ldap_conn, $user_dn, $user_data);
if (!$result) {
echo "创建用户失败: " . ldap_error($ldap_conn) . "\n";
ldap_close($ldap_conn);
exit;
} else {
echo "用户帐户已创建(已禁用)。\n";
}
// 2. 修改帐户以设置密码并启用它
$new_password_utf16le = iconv("UTF-8", "UTF-16LE", '"' . $password . '"');
$mod_data = array(
"unicodePwd" => array($new_password_utf16le),
"userAccountControl" => array(512) //普通帐户,已启用
);
//修改用户
$result = ldap_modify($ldap_conn, $user_dn, $mod_data);
if ($result) {
echo "用户帐户已启用且密码已设置。\n";
} else {
echo "无法启用用户/设置密码: " . ldap_error($ldap_conn) . "\n";
}
ldap_close($ldap_conn);
?>
主要改进和说明:
两步过程: 该代码现在在一个步骤中创建用户(没有密码或启用它),然后 修改帐户以设置密码并启用它。 这是解决 WILL_NOT_PERFORM
错误的常见方法 47。
错误处理: 改进的错误消息,以帮助诊断问题。 在每次 LDAP 操作后使用 ldap_error()
以提供更具体的错误详细信息。
LDAP 关闭: 在所有操作完成后调用 ldap_close()
函数以关闭连接。
userAccountControl
值: 在第二步中,userAccountControl
属性设置为 512
。
密码编码: 密码编码与以前相同,但在修改阶段应用。
通过分离用户创建和密码设置步骤,你可以绕过一些触发 WILL_NOT_PERFORM
错误的常见问题 47。 如果你仍然 遇到问题,请仔细检查密码复杂度要求以及用于 LDAP 绑定的帐户的权限
php ldap 创建 ad 帐户
在 PHP 中通过 LDAP 创建 Active Directory (AD) 账户需要遵循以下步骤。请注意,操作前需确保已安装并启用 PHP 的 ldap
扩展。
使用 ldap_connect()
建立与 AD 服务器的连接,并通过管理员账号绑定。
$ldapServer = 'ldap://ad.example.com'; // AD 服务器地址 $ldapPort = 389; // 默认 LDAP 端口 $adminUser = 'cn=admin,dc=example,dc=com'; // 管理员 DN $adminPass = 'admin_password'; // 管理员密码 // 连接 AD $ldapConn = ldap_connect($ldapServer, $ldapPort); if (!$ldapConn) { die("无法连接 AD 服务器"); } // 设置协议版本(通常需要 LDAPv3) ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0); // 绑定管理员账号 $bind = ldap_bind($ldapConn, $adminUser, $adminPass); if (!$bind) { die("绑定失败: " . ldap_error($ldapConn)); }
AD 用户条目需要包含必填属性,如 sAMAccountName
、userPrincipalName
等。注意密码需按 AD 要求编码。
$username = 'johndoe'; // 用户名(sAMAccountName) $userDn = 'cn=' . $username . ',ou=Users,dc=example,dc=com'; // 新用户的 DN // 用户属性数组 $userInfo = [ 'objectClass' => ['top', 'person', 'organizationalPerson', 'user'], 'cn' => $username,//不懂,为什么有了cn,会报错 Invalid DN syntax ,,,00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of 'sAMAccountName' => $username, 'userPrincipalName' => $username . '@example.com', 'displayName' => 'John Doe', 'givenName' => 'John', 'sn' => 'Doe', 'mail' => 'johndoe@example.com', 'userAccountControl' => 512, // 512 表示启用账户(正常状态) ]; // 设置密码(需 UTF-16LE 编码并用双引号包裹) $password = 'P@ssw0rd!'; $encodedPassword = '"' . $password . '"'; $userInfo['unicodePwd'] = iconv('UTF-8', 'UTF-16LE', $encodedPassword);
使用 ldap_add()
将用户添加到 AD 中。
// 添加用户条目 $result = ldap_add($ldapConn, $userDn, $userInfo); if (!$result) { die("创建用户失败: " . ldap_error($ldapConn)); } echo "用户 $username 创建成功!"; // 关闭连接 ldap_unbind($ldapConn);
LDAPS 加密:
生产环境建议使用 LDAPS (端口 636) 加密通信。需配置 ldap://
为 ldaps://
并确保服务器证书可信。
密码策略:
AD 密码需符合复杂度要求(如长度、大小写、符号)。示例中的 P@ssw0rd!
仅为演示,实际应使用安全密码。
错误处理:
检查每一步的返回值,使用 ldap_error()
捕获错误信息。
账户启用:userAccountControl
的 512
表示启用账户。若设为 514
则为禁用状态。
<?php
// AD 连接配置
$ldapServer = 'ldap://ad.example.com';
$ldapPort = 389;
$adminUser = 'cn=admin,dc=example,dc=com';
$adminPass = 'admin_password';
// 连接 AD
$ldapConn = ldap_connect($ldapServer, $ldapPort);
if (!$ldapConn) die("连接失败");
// 设置选项并绑定
ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);
$bind = ldap_bind($ldapConn, $adminUser, $adminPass);
if (!$bind) die("绑定失败: " . ldap_error($ldapConn));
// 新用户信息
$username = 'johndoe';
$userDn = "cn=$username,ou=Users,dc=example,dc=com";
$password = 'P@ssw0rd!';
$userInfo = [
'objectClass' => ['top', 'person', 'organizationalPerson', 'user'],
'cn' => $username, //不懂,为什么有了cn,会报错 Invalid DN syntax ,,,00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of
'sAMAccountName' => $username,
'userPrincipalName' => "$username@example.com",
'displayName' => 'John Doe',
'givenName' => 'John',
'sn' => 'Doe',
'mail' => 'johndoe@example.com',
'userAccountControl' => 512,
'unicodePwd' => iconv('UTF-8', 'UTF-16LE', "\"$password\"")
];
// 创建用户
$result = ldap_add($ldapConn, $userDn, $userInfo);
if (!$result) die("创建失败: " . ldap_error($ldapConn));
echo "用户 $username 已成功创建!";
ldap_unbind($ldapConn);
?>
连接失败:检查防火墙、AD 服务器地址/端口。
绑定失败:确认管理员 DN 和密码正确。
属性错误:确保 objectClass
包含必要的类,属性符合 AD 架构。
密码问题:确保密码编码正确且符合复杂度策略,使用 LDAPS 传输密码。
建议在测试环境中验证代码后再部署到生产环境。
ldap_add 会报错 Invalid DN syntax
在使用 ldap_add
创建 AD 账户时遇到 “Invalid DN syntax” 错误,通常是因为提供的 Distinguished Name (DN) 不符合 LDAP 格式要求。以下是可能的原因和解决方案:
DN 必须遵循严格的层次结构,例如:cn=用户名,ou=组织单元,dc=域组件1,dc=域组件2
常见格式错误包括:
缺少层级分隔符(逗号 ,
):例如 cn=userou=Users,dc=example,dc=com
(缺少逗号)。
层级顺序错误:例如 ou=Users,cn=user,dc=example,dc=com
(cn
应该在 ou
之后)。
特殊字符未转义:如果用户名或 OU 名称包含 ,
、=
、+
、<
、>
、#
、;
或空格,必须用反斜杠 \
转义。
示例:
若用户名为 Doe, John
,应写为 cn=Doe\, John,ou=Users,dc=example,dc=com
。
如果指定的 ou=Users,dc=example,dc=com
在 AD 中不存在,操作会失败。
解决方法:
使用 LDAP 管理工具(如 ADUC、Apache Directory Studio) 确认 OU 路径存在。
如果 OU 不存在,先创建 OU 或调整 DN 到有效路径。
在动态生成 DN 时,如果变量包含空格或特殊字符,需确保正确转义。
错误示例(未处理特殊字符):
$username = "John Doe"; // 包含空格 $userDn = "cn=$username,ou=Users,dc=example,dc=com"; // 实际 DN 为 `cn=John Doe,ou=Users,dc=example,dc=com`(空格未转义)
正确做法(转义特殊字符):
$username = "John Doe"; // 转义空格(AD 通常允许空格,但某些场景需要转义) $escapedUsername = ldap_escape($username, "", LDAP_ESCAPE_DN); $userDn = "cn=$escapedUsername,ou=Users,dc=example,dc=com";
ldap_escape()
函数处理动态内容
PHP 的 ldap_escape()
函数可自动转义 DN 中的特殊字符:
$username = "Doe, John"; $ou = "Sales+IT"; // 包含特殊字符 `+` // 转义用户名和 OU $escapedUsername = ldap_escape($username, "", LDAP_ESCAPE_DN); $escapedOU = ldap_escape($ou, "", LDAP_ESCAPE_DN); // 拼接 DN $userDn = "cn=$escapedUsername,ou=$escapedOU,dc=example,dc=com";
在代码中打印生成的 DN,检查是否符合格式:
$userDn = "cn=$username,ou=Users,dc=example,dc=com"; echo "尝试创建的 DN: " . $userDn; // 输出后检查格式
// 假设 $username 可能包含特殊字符 $username = "John Doe (Admin)"; $baseDn = "ou=Users,dc=example,dc=com"; // 转义用户名中的特殊字符 $escapedUsername = ldap_escape($username, "", LDAP_ESCAPE_DN); // 拼接 DN $userDn = "cn=$escapedUsername,$baseDn"; // 检查 DN 格式 var_dump($userDn); // 正确示例:string(35) "cn=John Doe \28Admin\29,ou=Users,dc=example,dc=com" // 使用 ldap_add $result = ldap_add($ldapConn, $userDn, $userInfo);
检查管理员绑定 DN:
确保管理员账号的 DN 格式正确,例如 cn=admin,dc=example,dc=com
,而非 admin@example.com
(除非是 UPN 格式绑定)。
确认 OU 的 distinguishedName:
在 AD 中,OU 的 DN 可能包含其他属性(如 OU=Users,DC=example,DC=com
),注意大小写是否匹配。
使用 LDAP 工具验证:
用 ldapsearch
或 GUI 工具直接尝试创建相同 DN,确认是否是代码问题:
ldapadd -x -H ldap://ad.example.com -D "cn=admin,dc=example,dc=com" -W -f user.ldif
user.ldif
内容:
dn: cn=testuser,ou=Users,dc=example,dc=com objectClass: user sAMAccountName: testuser
通过逐步检查 DN 格式、转义特殊字符并验证路径有效性,即可解决 “Invalid DN syntax” 错误
ldap_error($ldapConn) 太简单,,,ldap_add 如何获得 详细的报错信息
在 PHP 中通过 ldap_add
创建 AD 用户时,如果 ldap_error($ldapConn)
返回的报错信息过于简略(例如仅提示 Invalid DN syntax
),可以通过以下方法获取更详细的错误信息,帮助精准定位问题:
PHP 的 LDAP 扩展支持设置调试级别,通过 LDAP_OPT_DEBUG_LEVEL
记录详细的通信日志。
步骤:
在代码中启用调试模式:
// 设置调试级别(需在绑定前设置) ldap_set_option($ldapConn, LDAP_OPT_DEBUG_LEVEL, 7);
运行代码时,调试信息会直接输出到 PHP 错误日志 或 Web 服务器日志(如 Apache 的 error_log
)。
示例输出:
ldap_create ldap_bind_s ldap_sasl_bind_s ldap_send_initial_request ldap_new_connection ... ldap_write: send request with msgid 1 ldap_result: msgid 1, all 1 ldap_parse_result ldap_err2string: Invalid DN syntax (34)
除了 ldap_error()
,使用 ldap_errno()
获取错误代码,并结合 ldap_get_option()
提取服务器返回的扩展错误信息:
// 执行 ldap_add 后检查错误 if (!ldap_add($ldapConn, $userDn, $userInfo)) { $errorCode = ldap_errno($ldapConn); $errorMsg = ldap_error($ldapConn); // 尝试获取扩展错误信息(AD 服务器可能返回更多细节) ldap_get_option($ldapConn, LDAP_OPT_ERROR_STRING, $extendedError); die("错误代码: $errorCode\n错误信息: $errorMsg\n扩展信息: $extendedError"); }
输出示例:
错误代码: 34 错误信息: Invalid DN syntax 扩展信息: 0000208F: AtrErr: DSID-030F1141, #1: 0: 0000208F: DSID-030F1141, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att: unicodePwd (13)
如果扩展信息仍不清晰,可直接检查 AD 服务器的事件日志:
打开 Windows 服务器的 事件查看器(Event Viewer)。
导航到 Applications and Services Logs > Directory Service
。
查找与 LDAP 操作相关的错误事件,通常包含详细的失败原因(如属性约束、权限问题等)。
ldap_explode_dn()
分解 DN
如果怀疑 DN 格式错误,可以用 ldap_explode_dn()
分解并验证 DN 的每一部分:
$userDn = 'cn=johndoe,ou=Users,dc=example,dc=com'; $dnParts = ldap_explode_dn($userDn, 0); // 第二个参数 0 表示不解析属性值 if ($dnParts === false) { die("DN 格式无效"); } print_r($dnParts);
输出:
Array ( [0] => cn=johndoe [1] => ou=Users [2] => dc=example [3] => dc=com [count] => 4 )
通过 LDAP 命令行工具 或 GUI 工具 直接操作 AD,验证 DN 和属性的有效性:
使用 ldapadd
命令:
ldapadd -x -H ldap://ad.example.com -D "cn=admin,dc=example,dc=com" -W -f user.ldif
user.ldif
文件内容:
dn: cn=testuser,ou=Users,dc=example,dc=com objectClass: user sAMAccountName: testuser userPrincipalName: testuser@example.com unicodePwd:: "IgBQAEAAcwBzAHcAMAByAGQAIQAiAA==" # Base64 编码的密码
使用 Apache Directory Studio:
通过 GUI 工具直接创建条目,观察是否报错。
错误示例:CONSTRAINT_ATT_TYPE (unicodePwd)
表示密码不符合策略。
解决方案:
确保密码编码为 UTF-16LE
并用双引号包裹:
$password = 'P@ssw0rd!'; $encodedPassword = '"' . $password . '"'; $userInfo['unicodePwd'] = iconv('UTF-8', 'UTF-16LE', $encodedPassword);
错误示例:Insufficient access rights
或 00002098: SecErr: DSID-031A11C3
。
解决方案:
确保绑定用户(如管理员)有权限在目标 OU 下创建对象。
错误示例:OBJECT_CLASS_VIOLATION
表示缺少必填属性。
解决方案:
检查 objectClass
是否包含必要类(如 user
),并补全必填属性(如 sAMAccountName
)。
<?php
$ldapServer = 'ldap://ad.example.com';
$ldapPort = 389;
$adminUser = 'cn=admin,dc=example,dc=com';
$adminPass = 'admin_password';
// 连接并启用调试
$ldapConn = ldap_connect($ldapServer, $ldapPort);
ldap_set_option($ldapConn, LDAP_OPT_DEBUG_LEVEL, 7);
ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);
if (!ldap_bind($ldapConn, $adminUser, $adminPass)) {
die("绑定失败: " . ldap_error($ldapConn));
}
// 准备用户数据
$username = 'johndoe';
$userDn = "cn=$username,ou=Users,dc=example,dc=com";
$password = 'P@ssw0rd!';
$userInfo = [
'objectClass' => ['top', 'person', 'organizationalPerson', 'user'],
'cn' => $username, //不懂,为什么有了cn,会报错 Invalid DN syntax ,,,00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of
'sAMAccountName' => $username,
'userPrincipalName' => "$username@example.com",
'unicodePwd' => iconv('UTF-8', 'UTF-16LE', "\"$password\""),
'userAccountControl' => 512,
];
// 添加用户并捕获错误
if (!ldap_add($ldapConn, $userDn, $userInfo)) {
$errorCode = ldap_errno($ldapConn);
$errorMsg = ldap_error($ldapConn);
ldap_get_option($ldapConn, LDAP_OPT_ERROR_STRING, $extendedError);
echo "错误代码: $errorCode\n";
echo "错误信息: $errorMsg\n";
echo "扩展信息: $extendedError\n";
// 分解 DN 检查格式
$dnParts = ldap_explode_dn($userDn, 0);
if ($dnParts === false) {'
echo "DN 格式无效!";
} else {
echo "DN 分解结果: " . print_r($dnParts, true);
}
} else {
echo "用户创建成功!";
}
ldap_unbind($ldapConn);
?>
通过 启用调试日志、获取扩展错误信息 和 直接操作 AD 验证,可以精准定位 ldap_add
失败的原因。若问题仍无法解决,建议结合 AD 服务器日志和网络抓包(如 Wireshark)进一步分析。
ldap_errno($conn) 为 34 ,,,"00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of:
'CN=test6,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn'
我自己亲自做的 是因为 上面的 $userInfo 数组中 有一个键是 cn ,,把cn 键删掉就可以了.所以关于这一段内容可以不看了
根据错误信息 ldap_errno=34
和扩展错误 00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX)
,结合你的示例 DN CN=test6,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn
,问题可能由以下原因导致:
AD 要求 DN 中的某些字符(如中文、空格、逗号等)必须转义。
虽然中文本身不需要转义,但如果 OU 名称(如 bbbbbb集团
)在 AD 中的实际名称包含 隐藏的特殊字符(如 ,
、=
、#
等),需用反斜杠 \
转义。
假设 AD 中 OU 的实际名称包含逗号(如 bbbbbb,集团
),但代码中未转义:
$ou = "bbbbbb,集团"; // 未转义逗号 $userDn = "CN=test6,OU=$ou,DC=aaaaaaaa,DC=com,DC=cn"; // 实际 DN 为 `CN=test6,OU=bbbbbb,集团,DC=...`,逗号被解析为层级分隔符,导致语法错误
使用 ldap_escape()
对 OU 和 CN 的名称进行转义:
$ou = "bbbbbb集团"; // 转义 OU 名称中的特殊字符 $escapedOU = ldap_escape($ou, "", LDAP_ESCAPE_DN); $userDn = "CN=test6,OU=$escapedOU,DC=aaaaaaaa,DC=com,DC=cn";
错误信息中的 NameErr: DSID-03050EC7
表示 AD 服务器找不到指定的 OU 路径 OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn
。
使用 ldapsearch
检查 OU 是否存在:
ldapsearch -x -H ldap://AD服务器地址 -D "管理员DN" -W -b "DC=aaaaaaaa,DC=com,DC=cn" "(ou=bbbbbb集团)"
如果无返回结果,说明 OU 不存在。
通过 AD 管理工具(如 Active Directory Users and Computers)确认 OU 路径。
创建缺失的 OU:
通过代码或管理工具先创建 OU。
调整 DN 到有效路径:
确保 OU=bbbbbb集团
存在于 DC=aaaaaaaa,DC=com,DC=cn
下。
AD 的域名组件(DC)按层级从高到低排列,例如:DC=com,DC=cn
表示顶级域为 com.cn
,子域为 aaaaaaaa
,完整域名为 aaaaaaaa.com.cn
。
若实际域名是 aaaaaaaa.com
,则 DC 应为 DC=aaaaaaaa,DC=com
,而非 DC=aaaaaaaa,DC=com,DC=cn
。
通过 AD 服务器查询根域:
ldapsearch -x -H ldap://AD服务器地址 -D "管理员DN" -W -b "" -s base "(objectClass=*)" defaultNamingContext
返回的 defaultNamingContext
是 AD 的根域,例如 DC=aaaaaaaa,DC=com,DC=cn
。
确保代码中的 DC 顺序与根域一致。
如果 AD 服务器或 PHP 环境未正确处理 UTF-8 编码,中文字符可能被错误解析。
设置 LDAP 连接的字符集:
ldap_set_option($ldapConn, LDAP_OPT_ENCODING, LDAP_OPT_OFF);
确保 PHP 文件保存为 UTF-8 无 BOM 格式。
// 连接 AD 服务器 $ldapConn = ldap_connect("ldap://ad.aaaaaaaa.com.cn"); ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldapConn, LDAP_OPT_ENCODING, LDAP_OPT_OFF); // 处理中文字符 // 绑定管理员 ldap_bind($ldapConn, "cn=admin,dc=aaaaaaaa,dc=com,dc=cn", "password"); // 准备 DN(转义 OU 和 CN) $ou = "bbbbbb集团"; $username = "test6"; $escapedOU = ldap_escape($ou, "", LDAP_ESCAPE_DN); $escapedCN = ldap_escape($username, "", LDAP_ESCAPE_DN); $userDn = "CN=$escapedCN,OU=$escapedOU,DC=aaaaaaaa,DC=com,DC=cn"; // 检查 DN 格式 var_dump($userDn); // 输出:string(59) "CN=test6,OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn" // 添加用户 $userInfo = [ 'objectClass' => ['user'], 'sAMAccountName' => $username, 'userPrincipalName' => "$username@aaaaaaaa.com.cn", 'unicodePwd' => iconv('UTF-8', 'UTF-16LE', '"P@ssw0rd"'), 'userAccountControl' => 512 ]; if (!ldap_add($ldapConn, $userDn, $userInfo)) { $errorCode = ldap_errno($ldapConn); $errorMsg = ldap_error($ldapConn); ldap_get_option($ldapConn, LDAP_OPT_ERROR_STRING, $extendedError); die("错误代码: $errorCode\n错误信息: $errorMsg\n扩展信息: $extendedError"); } echo "用户创建成功!";
使用 ldap_escape()
转义所有动态内容:
确保 OU、CN 等名称中的特殊字符被正确处理。
验证 OU 路径:
通过 ldapsearch
或 AD 管理工具确认路径存在。
检查域名层级:
确保 DC 顺序与 AD 实际域名一致。
查看 AD 服务器日志:
在 Windows 事件查看器中搜索与 LDAP 操作相关的错误事件。
通过以上步骤,可解决 Invalid DN syntax
和 BAD_ATT_SYNTAX
问题。
Server is unwilling to perform
错误代号 53
0000001F: SvcErr: DSID-031A126A, problem 5003 (WILL_NOT_PERFORM), data 0
遇到 Server is unwilling to perform
(错误代码 53,扩展错误 0000001F: SvcErr: DSID-031A126A, problem 5003 (WILL_NOT_PERFORM)
,通常由以下原因导致:
Active Directory 要求密码必须符合复杂度规则(如长度、大小写字母、数字、符号),且 unicodePwd
属性需正确编码为 UTF-16LE。
$password = "123456"; // 密码太简单 $userInfo['unicodePwd'] = iconv('UTF-8', 'UTF-16LE', $password); // 未包裹双引号
使用符合复杂度的密码:
例如 P@ssw0rd2023!
。
正确编码密码:
密码需用双引号包裹后转换为 UTF-16LE。
$password = 'P@ssw0rd2023!'; $encodedPassword = '"' . $password . '"'; $userInfo['unicodePwd'] = iconv('UTF-8', 'UTF-16LE', $encodedPassword);
绑定账户(如管理员)可能没有在目标 OU 下创建用户的权限。
使用 AD 管理工具 确认绑定账户的权限:
右键点击目标 OU(如 OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn
)。
选择 属性 > 安全 > 高级,检查账户是否有 “创建子对象” 权限。
临时提升权限测试:
使用域管理员账户(如 administrator@aaaaaaaa.com.cn
)进行绑定。
$adminUser = 'administrator@aaaaaaaa.com.cn'; // 使用 UPN 格式 $adminPass = 'AdminPassword123!'; $ldapConn = ldap_connect("ldap://ad.aaaaaaaa.com.cn"); ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_bind($ldapConn, $adminUser, $adminPass);
userAccountControl
如果账户需要启用,userAccountControl
必须设为 512
;若还需密码永不过期,可设为 66048
(512 + 65536
)。
$userInfo = [ 'objectClass' => ['top', 'person', 'organizationalPerson', 'user'], 'sAMAccountName' => $username, 'userPrincipalName' => "$username@aaaaaaaa.com.cn", 'unicodePwd' => iconv('UTF-8', 'UTF-16LE', '"' . $password . '"'), 'userAccountControl' => 512, // 512 表示启用账户 ];
某些 AD 环境强制要求通过 LDAPS(端口 636) 修改敏感属性(如密码)。
使用 ldaps://
协议连接:
$ldapServer = 'ldaps://ad.aaaaaaaa.com.cn'; // 注意协议为 ldaps:// $ldapPort = 636; // LDAPS 默认端口 $ldapConn = ldap_connect($ldapServer, $ldapPort);
导入 AD 的 CA 证书:
在 PHP 环境中配置信任 AD 的证书,避免 SSL 验证失败:
// 忽略证书验证(仅测试环境使用) ldap_set_option($ldapConn, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
通过 Windows 事件查看器 获取详细错误原因:
打开 AD 服务器的 事件查看器。
导航至 Applications and Services Logs > Directory Service
。
查找与 LDAP 操作相关的错误事件,通常会包含具体拒绝原因(如密码策略、权限问题)。
<?php
// 配置参数
$ldapServer = 'ldaps://ad.aaaaaaaa.com.cn';
$ldapPort = 636;
$adminUser = 'administrator@aaaaaaaa.com.cn';
$adminPass = 'AdminPassword123!';
$baseDn = 'OU=bbbbbb集团,DC=aaaaaaaa,DC=com,DC=cn';
$username = 'test6';
$password = 'P@ssw0rd2023!'; // 符合复杂度要求的密码
// 连接 AD(LDAPS)
$ldapConn = ldap_connect($ldapServer, $ldapPort);
if (!$ldapConn) die("连接失败");
// 设置选项
ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, 0);
ldap_set_option($ldapConn, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER); // 忽略证书验证
// 绑定管理员
if (!ldap_bind($ldapConn, $adminUser, $adminPass)) {
die("绑定失败: " . ldap_error($ldapConn));
}
// 准备用户 DN 和属性
$userDn = "CN=$username,$baseDn";
$encodedPassword = iconv('UTF-8', 'UTF-16LE', '"' . $password . '"');
$userInfo = [
'objectClass' => ['top', 'person', 'organizationalPerson', 'user'],
'cn' => $username, //不懂,为什么有了cn,会报错 Invalid DN syntax ,,,00002081: NameErr: DSID-03050EC7, problem 2003 (BAD_ATT_SYNTAX), data 0, best match of
'sAMAccountName' => $username,
'userPrincipalName' => "$username@aaaaaaaa.com.cn",
'unicodePwd' => $encodedPassword,
'userAccountControl' => 512,
];
// 创建用户
if (!ldap_add($ldapConn, $userDn, $userInfo)) {
$errorCode = ldap_errno($ldapConn);
$errorMsg = ldap_error($ldapConn);
ldap_get_option($ldapConn, LDAP_OPT_ERROR_STRING, $extendedError);
die("错误代码: $errorCode\n错误信息: $errorMsg\n扩展信息: $extendedError");
}
echo "用户 $username 创建成功!";
ldap_unbind($ldapConn);
?>
密码复杂度与编码:
确保密码长度 ≥ 8,包含大小写、数字、符号。
正确使用 iconv('UTF-8', 'UTF-16LE', '"密码"')
编码。
权限验证:
使用域管理员账户绑定。
检查目标 OU 的 ACL 权限。
强制使用 LDAPS:
将协议改为 ldaps://
,端口 636
。
服务器日志分析:
通过事件查看器定位具体拒绝原因。
通过上述步骤,可解决 WILL_NOT_PERFORM
错误。如问题仍未解决,需结合服务器日志和网络抓包进一步分析。
'userAccountControl' => 512, 与 'userAccountControl' => 514 的区别
在 Active Directory 中,userAccountControl
是一个用于控制用户账户状态的 位掩码属性(Bitmask)。值 512
和 514
是两种常见配置,对应账户的启用和禁用状态。以下是它们的核心区别和用法:
userAccountControl = 512
含义:
表示账户处于 正常启用状态,可以登录、访问资源。
二进制分解:512
的二进制为 1000000000
,对应第 9 位(UF_NORMAL_ACCOUNT) 被激活。
典型场景:
创建新用户时,若希望用户 立即生效,应设置 userAccountControl
为 512
。
userAccountControl = 514
含义:
表示账户处于 禁用状态(用户无法登录),但账户本身存在。514
的二进制为 1000000010
,由以下两个标志位组成:
512
(1000000000
): UF_NORMAL_ACCOUNT(正常账户类型)
2
(0000000010
): UF_ACCOUNTDISABLE(账户禁用)
典型场景:
创建用户时暂时禁用账户(例如需要后续手动激活),或禁用已有账户。
属性值 | 账户状态 | 二进制分解 | 适用场景 |
---|---|---|---|
512 | 启用 | 1000000000
| 用户可正常登录 |
514 | 禁用 | 1000000010
| 创建时禁用,或临时锁定账户 |
userAccountControl
支持通过 位运算 组合多个标志:
512 + 65536 = 66048
启用账户 (512
) + 密码永不过期 (65536
,即 UF_DONT_EXPIRE_PASSWD
)。
512 + 32 = 544
启用账户 (512
) + 用户下次登录需修改密码 (32
,即 UF_PASSWORD_EXPIRED
)。
$userInfo = [ 'objectClass' => ['user'], 'sAMAccountName' => 'johndoe', 'unicodePwd' => iconv('UTF-8', 'UTF-16LE', '"P@ssw0rd"'), 'userAccountControl' => 512, // 启用账户 ];
$userInfo = [ 'objectClass' => ['user'], 'sAMAccountName' => 'johndoe', 'unicodePwd' => iconv('UTF-8', 'UTF-16LE', '"P@ssw0rd"'), 'userAccountControl' => 514, // 创建时禁用 ];
$userInfo['userAccountControl'] = 66048; // 512 + 65536
通过 AD 管理工具:
打开 Active Directory Users and Computers
,检查用户属性中的 “账户已禁用” 复选框状态。
通过 LDAP 查询:
使用 ldapsearch
或代码读取 userAccountControl
值:
ldapsearch -x -H ldap://ad.example.com -D "admin@example.com" -W -b "cn=johndoe,ou=Users,dc=example,dc=com" userAccountControl
权限要求:
修改 userAccountControl
需要足够的权限(如域管理员)。
密码策略:
即使账户启用,密码仍需符合复杂度要求。
LDAPS 加密:
敏感操作建议使用 ldaps://
协议(端口 636)。
掌握 userAccountControl
的标志位组合,可精准控制账户行为(如禁用、密码策略、登录限制等)。