What's LDAP
(一)在介绍什么是LDAP之前,我们先来了解一个东西:“什么是目录服务?”
目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,支持过滤功能。
是动态的,灵活的,易扩展的。
(二)了解完目录服务后,我们再来看看LDAP的介绍:
LDAP(Light Directory Access Portocol),它是基于X.500标准的轻量级目录访问协议。
目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。
目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。
LDAP目录服务是由目录数据库和一套访问协议组成的系统。
LDAP是开放的Internet标准,支持跨平台的Internet协议,在业界中得到广泛认可的,并且市场上或者开源社区上的大多产品都加入了对LDAP的支持,因此对于这类系统,不需单独定制,只需要通过LDAP做简单的配置就可以与服务器做认证交互。“简单粗暴”,可以大大降低重复开发和对接的成本。
(三)目录树概念
1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。
2.条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。
3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。
4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性。
(四)DC、UID、OU、CN、SN、DN、RDN
DC、UID、OU、CN、SN、DN、RDN这些关键词在LDAP中出现的频率相当之高,其所代表的含义一定要清楚。
关键字 | 英文全称 | 含义 |
---|---|---|
DC | Domain Component | 域名部分 |
UID | User ID | 用户ID |
OU | Organization Unit | 组织单位 |
CN | Common Name | 公共名称 |
SN | Surname | 姓 |
DN | Distinguished Name | 数据唯一标识符 |
RDN | Relative DN | 相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分 |
这里要着重看DC、CN、DN这三个,DC是域名部分,以sub.dimain.com为例,用DC表示就是“DC=sub,DC=domain,DC=com”,这里的DN和咱们的URL相似,他是由包含多个DC和CN的字符串构成的,比如这个DN
$DN="CN=User,DC=sub,DC=domain,DC=com";
PHP LDAP
协会网站是PHP环境,PHP有一个LDAP拓展可以直接操作AD域,有了这个拓展,升级会员系统为AD域统一认证方便了不少。
LDAP拓展在PHP中默认是不安装的,全新安装PHP时可以通过添加“--with-ldap”选项安装LDAP拓展。对于已经安装过PHP环境,需要对PHP添加LDAP支持的,可以到PHP官网,下载对应版本的源码,源码中包含有LDAP,编译安装即可。以PHP5.6.30,安装目录为/www/server/php/56/为例:
[root@localhost ~] wget https://www.php.net/distributions/php-5.6.30.tar.gz #下载源码包 [root@localhost ~] tar -xzvf php-5.6.30.tar.gz #解压 [root@localhost ~] cd php-5.6.30/ext/ldap/ #进入LDAP模块源码目录 [root@localhost ~] /www/server/php/56/bin/phpize #使用phpize准备PHP拓展库编译环境 [root@localhost ~] ./configure --with-php-config=/www/server/php/56/bin/php-config #配置编译 [root@localhost ~] make && make install #编译安装
正常情况下编译成功后会在PHP目录lib/php/extensions下产生一个ldap.so的文件,编辑php.ini文件在文件末尾添加extension =ldap.so即可。
遇到的报错和解决方法
configure: error: Cannot find ldap.h
[root@localhost ~] yum install openldap openldap-devel
configure: error: Cannot find ldap libraries in /usr/lib
[root@localhost ~] cp -frp /usr/lib64/libldap* /usr/lib/
LDAP Browser
对于一个从来没接触过LDAP的我来说,各种介绍在怎么丰富,也不及亲自上手看一看,下载一个LDAP Browser,浏览一下AD域控上面的LDAP里面记录了什么,怎么记录的对刚上手AD域控的同学来说还是很有帮助的。这里推荐的是Free LDAP Browser For Windows。
Window Active Directory将用户信息存储在了CN=Users中,网上各种教程有说在Account中的,有说查UID才能查得到的……自己翻了一遍AD域控才确定是在Users中,坑……满地都是坑……
初出茅庐
先写一个基本的程序测试AD域的连接。
<?php //LDAP连接测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldap://192.168.1.2:389"; //用户唯一辨识符,也可以使用administrator@sub.domain.com这种格式 $user_dn="CN=Administrator,CN=Users,DC=lab,DC=pypy,DC=fun"; //用户密码 $user_password="password"; echo "<h3>LDAP query test</h3>"; echo "Connecting ..."; //创建一个LDAP连接 $ds=ldap_connect($server_host); echo "connect result is " . $ds . "<br />"; if ($ds) { echo "Binding ..."; //绑定连接,如果用户唯一辨识符和密码不正确,将无法绑定 $r=ldap_bind($ds,$user_dn,$user_password); echo "Bind result is " . $r . "<br />"; echo "Searching for Users..."; //查询用户 $sr=ldap_search($ds, "CN=Users,DC=lab,DC=pypy,DC=fun", "CN=Administrator"); echo "Search result is " . $sr . "<br />"; echo "Number of entries returned is " . ldap_count_entries($ds, $sr) . "<br />"; echo "Getting entries ...<p>"; $info = ldap_get_entries($ds, $sr); echo "Data for " . $info["count"] . " items returned:<p>"; for ($i=0; $i<$info["count"]; $i++) { echo "dn is: " . $info[$i]["dn"] . "<br />"; echo "first cn entry is: " . $info[$i]["cn"][0] . "<br />"; echo "first email entry is: " . $info[$i]["mail"][0] . "<br /><hr />"; } echo "Closing connection"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
可以看到,成功连接了,并查找到了这个Administrator的User。
登陆验证
对这个程序稍加修改就能变成我们的登陆验证程序。
<?php //LDAP连接测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldap://10.60.60.254:389"; //需要认证的用户账户 $user_post="upn_login@lab.pypy.fun"; //用户密码 $user_password="password"; echo "<h3>LDAP query test</h3>"; echo "Connecting ..."; //创建一个LDAP连接 $ds=ldap_connect($server_host); if ($ds) { echo "connect result is " . $ds . "<br />"; ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);//声明使用版本3 ldap_set_option($ds, LDAP_OPT_REFERRALS, 0); // Binding to ldap server echo "Binding ..."; $r=@ldap_bind($ds,$user_post,$user_password); ldap_get_option($ds, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extended_error); if(!$error){ echo '登陆成功</br>'; echo "Bind result is " . $r . "<br />"; echo "查询用户组信息</br>"; $user_name=explode('@',$user_post); $sr=ldap_search($ds, "CN=Users,DC=lab,DC=pypy,DC=fun", "CN=".$user_name[0]); $info = ldap_get_entries($ds, $sr); echo "分组信息为"; echo($info[0]['memberof'][$info[0]['memberof']['count']-1]); }else{ echo "登陆失败:".$error."</br>"; } echo "Closing connection"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
替换网站现有的相关认证代码就可以了。
以下是试验结果
升级到LDAPS
坑……满地的坑……
LDAP只能查询AD域控制器中的信息,无法修改内容,若要修改,必须用SSL加密的LDAP,也就是LDAPS。
所以,我又踏上了把LDAP升级成LDAPS的不归路。
先耍个小聪明,直接改协议怎么样:
$server_host="ldap://10.60.60.254:389";
改为:
$server_host="ldaps://10.60.60.254:636";
显然,这是行不通的,报错:
ldap_bind: Can't contact LDAP server
首先,要确保我的AD域控是支持LDAPS的,然鹅,我并没有部署SSL,先前只是部署了AD DS。并没有部署AD CS(证书服务)。若要AD域控使用SSL加密,则必须安装AD CS证书服务。
安装AD CS证书服务跟安装AD DS域控制器一样,详情参考《Windows Server 2016 安装AD并开启SSL》这篇总结性的博文。
LDAP on SSL部署之后可以用Free LDAP Browser尝试SSL连接。
服务器那边加了证书,SSL加密也能用了,PHP这边再用$server_host="ldaps://10.60.60.254:636";试一下吧,结果嘛……
ldap_bind: Can't contact LDAP server
查阅了PHP官网,Andrew(a.whyte@cqu.edu.au)的一个Note的帮助很大。
1.要确保OpenLDAP编译安装是受OpenSSL的支持。
2.导出AD根证书并且配置OpenSSL信任该根证书
3.要配置OpenLDAP信任根证书
4.要在Web根目录中创建.ldaprc配置文件以便PHP可以获取OpenLDAP相关配置。
步骤有点多,一步一步来,关于OpenSSL的问题,我的OpenLDAP是用yum包管理器安装的,应该不存在这个问题。跳过。如果你是手动安装的,记得检查。
到AD域控上面,运行mmc.exe,添加证书管理单元
导出格式为Base64编码。
将导出的adCA.cer上传到/usr/local/openssl/certs/下面,并执行c_rehash
[root@localhost ~] mv adCA.cer /usr/local/openssl/certs/ [root@localhost ~] /usr/local/openssl/bin/c_rehash
接下来就是配置OpenLDAP了,编辑/etc/openldap/ldap.conf
#--begin-- # Instruct client to NOT request a server's cert. TLS_REQCERT never # Define location of CA Cert TLS_CACERT /usr/local/openssl/certs/adCA.cer TLS_CACERTDIR /usr/local/openssl/certs #--end--
此外,你还要在Apache网页根目录下建立一个.ldaprc的文件存储以上配置方便PHP读取OpenLDAP配置
[root@localhost ~] cp /etc/openldap/ldap.conf /www/wwwroot/ldap.lab.pypy.fun/
如果你的apache中没有对站点定义HOME目录的话,还要在站点的you-apache-site.conf中设置HOME目录
SetEnv HOME "/www/wwwroot/ldap.pypy.fun
配置完成后重启服务器。
记得连接时将IP改为域名来连接,不然要报错。使用
$server_host=ldaps://ad.lab.pypy.fun:636
来进行连接,终于出来了。我太难了……
修改密码
终于走到了这一步,可以尝试修改密码了。然鹅通过LDAP设置AD中用户密码必须满足非常严格的三个条件,否则会提示Warning:server is unwilling to perform。
1.必须使用SSL方式连接AD(ldaps://);
2.密码必须使用引号括起来;
3.引号中的密码必须使用16位unicode编码(UTF-16LE);
对于问题1,LDAPS我已经升级好了。问题2在新设置的密码前后加括号就行了,编码的问题可以用iconv转换就行了。
PS.说出来的坑都不再是坑,鬼知道我摸索了多久(:
最终测试程序如下:
<?php //LDAP改密测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldaps://ad.lab.pypy.fun:636"; //用于连接LDAP的管理用户唯一辨识符,也可以使用administrator@sub.domain.com这种格式 $manager_user_dn="CN=Administrator,CN=Users,DC=lab,DC=pypy,DC=fun"; //用户密码 $user_password="password"; //需要修改密码的目标用户DN $target_user_dn="CN=test_account,CN=Users,DC=lab,DC=pypy,DC=fun"; $target_pass="newPass123"; echo "<h3>LDAPS Change Password test</h3>"; echo "Server Host is:".$server_host."</br>"; echo "Manager User DN is:".$manager_user_dn."</br>"; echo "Target User DN is:".$target_user_dn."</br>"; echo "Connecting ...</br>"; //创建一个LDAP连接 $ds=ldap_connect($server_host); echo "connect result is " . $ds . "</br>"; if ($ds) { echo "Binding ...</br>"; //绑定连接,如果用户唯一辨识符和密码不正确,将无法绑定 $r=ldap_bind($ds,$manager_user_dn,$user_password); echo "Bind result is " . $r . "</br>"; echo "目标用户: " . $target_user_dn . "</br>"; //改密码 //编码 $target_pass='"'.$target_pass.'"'; $newPass = iconv( 'UTF-8', 'UTF-16LE', $target_pass ); //通过重写unicodepwd值来更改密码 $userdata["unicodepwd"] = $newPass; $result = ldap_mod_replace($ds,$target_user_dn,$userdata); if($result===true){ echo "修改密码成功!</br>"; }else{ echo "修改密码失败!</br>"; var_dump($result); echo "</br>"; } echo "Closing connection</br>"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
让test_account账户从加入域的计算机上注销,再次登陆改用新密码登陆,可以成功登陆。
另外我在查找资料的时候还看见了一个关于改密码之后,老密码还能使用的问题的帖子,记下来先,需要的时候可以去看看>>https://blog.51cto.com/lidongni/1760480
添加账户
添加账户也是非常常见的操作,每年纳新都有好多同学加入我们社团,也不可能挨个去添加到域里面,也是直接改网站注册模块,使之直接注册到域控里面。由于前面把坑踩得差不多了,这里也简单了许多,安装AD域控里面用户的数据结构,像添加数据库那样添加进去就行了。
然鹅……是我太小看它了……
加数据很好加用ldap_add()函数添加就行了,可是这数据结构可不是一般的……
/** * 表分类信息,保持不变即可 */ $userdata["objectClass"][0] = 'top'; $userdata["objectClass"][1] = 'person'; $userdata["objectClass"][2] = 'organizationalPerson'; $userdata["objectClass"][3] = 'user'; $userdata["instanceType"] = 4; /** * UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。 * 一个用户帐户名(有时称为“用户登录名”) * 和一个域名(标识用户帐户所在的域), * 这是登录到Windows域的标准用法。 * 格式是: xiaowen@azureyun.com (类电子邮件地址)。 * SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象, * 实际上是命名符号“Domain\LogonName ”中使用的旧NetBIOS表单, * 该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致, * 即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。 * 并且应该与CN保持相同。 * * 格式规定 * 不能超过20个字符,不允许出现\ / [] :; | =,+ *?<> @等特殊字符; */ $userdata["sAMAccountName"] = "NewUser"; $userdata["userPrincipalName"] = "NewUser"."@domain.com"; /** * name:账户名称,应与sAMAccountName保持一致 */ $userdata["name"] = "NewUser"; /** * userAccountControl记录了用户的AD账号的很多属性信息,该属性标志是累积性的。 * 也就是各属性相加之和。常见的有以下几种属性: * 禁用账户-----------------2 * 需要主文件夹-------------8 * 不需要密码--------------32 * 不能更改密码-------------64 * 使用可逆加密存储密码-----128 * 默认账户类型------------512 * 密码永不过期----------65536 * * 没有声明该值为514即默认账户类型+禁用账户 * 设为66048即为默认账户类型+密码永不过期 */ $userdata["userAccountControl"] = 66048; $userdata["displayName"] = '张三';//用户姓名 $userdata["company"] = '公司名称'; $userdata["department"] = '部门名称'; $userdata["physicalDeliveryOfficeName"] = '办公室'; $userdata["title"] = '职务'; $userdata["wWWHomePage"] = 'http://blog.tianjinkun.com';//个人主页 $userdata["description"] = '描述'; $userdata["telephoneNumber"] = '电话号码'; $userdata["ipPhone"] = 'IP电话,可以用来存储QQ号码';
摸清楚结构了,一切都好说,添加一个账户试试:
<?php //LDAPS添加用户测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldaps://ad.lab.pypy.fun:636"; //用于连接LDAP的管理用户唯一辨识符,也可以使用administrator@sub.domain.com这种格式 $manager_user_dn="CN=Administrator,CN=Users,DC=lab,DC=pypy,DC=fun"; //用户密码 $user_password="password"; //需要添加目标用户 //设要添加的用户为17100001@lab.pypy.fun $User_account='17100001'; $target_user_dn="CN=".$User_account.",CN=Users,DC=lab,DC=pypy,DC=fun"; /** * 表分类信息,保持不变即可 */ $userdata["objectClass"][0] = 'top'; $userdata["objectClass"][1] = 'person'; $userdata["objectClass"][2] = 'organizationalPerson'; $userdata["objectClass"][3] = 'user'; $userdata["instanceType"] = 4; /** * UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。 * 一个用户帐户名(有时称为“用户登录名”) * 和一个域名(标识用户帐户所在的域), * 这是登录到Windows域的标准用法。 * 格式是: xiaowen@azureyun.com (类电子邮件地址)。 * SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象, * 实际上是命名符号“Domain\LogonName ”中使用的旧NetBIOS表单, * 该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致, * 即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。 * 并且应该与CN保持相同。 * * 格式规定 * 不能超过20个字符,不允许出现\ / [] :; | =,+ *?<> @等特殊字符; */ $userdata["sAMAccountName"] = $User_account; $userdata["userPrincipalName"] = $User_account."@lab.pypy.fun"; /** * name:账户名称,应与sAMAccountName保持一致 */ $userdata["name"] = $User_account; /** * userAccountControl记录了用户的AD账号的很多属性信息,该属性标志是累积性的。 * 也就是各属性相加之和。常见的有以下几种属性: * 禁用账户-----------------2 * 需要主文件夹-------------8 * 不需要密码--------------32 * 不能更改密码-------------64 * 使用可逆加密存储密码-----128 * 默认账户类型------------512 * 密码永不过期----------65536 * * 没有声明该值为514即默认账户类型+禁用账户 * 设为66048即为默认账户类型+密码永不过期 */ $userdata["userAccountControl"] = 66048; $userdata["displayName"] = '张三';//用户姓名 $userdata["company"] = '公司名称'; $userdata["department"] = '部门名称'; $userdata["physicalDeliveryOfficeName"] = '办公室'; $userdata["title"] = '职务'; $userdata["wWWHomePage"] = 'http://blog.tianjinkun.com';//个人主页 $userdata["mail"] = 'mail@test.com'; $userdata["description"] = '个人描述'; $userdata["telephoneNumber"] = '110'; $userdata["ipPhone"] = '8888888';//IP电话,可以用来存储QQ号码 echo "<h3>LDAPS Add Account test</h3>"; echo "Server Host is:".$server_host."</br>"; echo "Manager User DN is:".$manager_user_dn."</br>"; echo "Target User DN is:".$target_user_dn."</br>"; echo "Connecting ...</br>"; //创建一个LDAP连接 $ds=ldap_connect($server_host); echo "connect result is " . $ds . "</br>"; if ($ds) { echo "Binding ...</br>"; //绑定连接,如果用户唯一辨识符和密码不正确,将无法绑定 $r=ldap_bind($ds,$manager_user_dn,$user_password); echo "Bind result is " . $r . "</br>"; echo "目标用户: " . $target_user_dn . "</br>"; $result = ldap_add($ds,$target_user_dn,$userdata); if($result===true){ echo "添加成功!</br>"; }else{ echo "添加失败!</br>"; var_dump($result); echo "</br>"; } echo "Closing connection</br>"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
理论上运行是没有问题的,运行一下,便得到了报错:
Warning: ldap_add(): server is unwilling to perform in /www/wwwroot/ldap.pypy.fun/add.php on line 99
添加失败!
当我把userAccountControl这个值屏蔽后便可以正常写入了,为什么呢……
一番研究之后找到了原因,我没有为这个账户设置密码!所以添加账户的操作要分开来做,添加账户信息>设置密码>修改userAccountControl,启用账户。
修改之后的代码如下:
<?php //LDAPS添加用户测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldaps://ad.lab.pypy.fun:636"; //用于连接LDAP的管理用户唯一辨识符,也可以使用administrator@sub.domain.com这种格式 $manager_user_dn="CN=Administrator,CN=Users,DC=lab,DC=pypy,DC=fun"; //用户密码 $user_password="password"; //需要添加目标用户 //设要添加的用户为17100001@lab.pypy.fun $User_account='17100001'; $target_user_dn="CN=".$User_account.",CN=Users,DC=lab,DC=pypy,DC=fun"; /** * 表分类信息,保持不变即可 */ $userdata["objectClass"][0] = 'top'; $userdata["objectClass"][1] = 'person'; $userdata["objectClass"][2] = 'organizationalPerson'; $userdata["objectClass"][3] = 'user'; $userdata["instanceType"] = 4; /** * UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。 * 一个用户帐户名(有时称为“用户登录名”) * 和一个域名(标识用户帐户所在的域), * 这是登录到Windows域的标准用法。 * 格式是: xiaowen@azureyun.com (类电子邮件地址)。 * SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象, * 实际上是命名符号“Domain\LogonName ”中使用的旧NetBIOS表单, * 该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致, * 即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。 * 并且应该与CN保持相同。 * * 格式规定 * 不能超过20个字符,不允许出现\ / [] :; | =,+ *?<> @等特殊字符; */ $userdata["sAMAccountName"] = $User_account; $userdata["userPrincipalName"] = $User_account."@lab.pypy.fun"; /** * name:账户名称,应与sAMAccountName保持一致 */ $userdata["name"] = $User_account; /** * userAccountControl记录了用户的AD账号的很多属性信息,该属性标志是累积性的。 * 也就是各属性相加之和。常见的有以下几种属性: * 禁用账户-----------------2 * 需要主文件夹-------------8 * 不需要密码--------------32 * 不能更改密码-------------64 * 使用可逆加密存储密码-----128 * 默认账户类型------------512 * 密码永不过期----------65536 * * 没有声明该值为514即默认账户类型+禁用账户 * 设为66048即为默认账户类型+密码永不过期 */ //$userdata["userAccountControl"] = 66048; $userdata["displayName"] = '张三';//用户姓名 $userdata["company"] = '公司名称'; $userdata["department"] = '部门名称'; $userdata["physicalDeliveryOfficeName"] = '办公室'; $userdata["title"] = '职务'; $userdata["wWWHomePage"] = 'http://blog.tianjinkun.com';//个人主页 $userdata["mail"] = 'mail@test.com'; $userdata["description"] = '个人描述'; $userdata["telephoneNumber"] = '110'; $userdata["ipPhone"] = '8888888';//IP电话,可以用来存储QQ号码 echo "<h3>LDAPS Add Account test</h3>"; echo "Server Host is:".$server_host."</br>"; echo "Manager User DN is:".$manager_user_dn."</br>"; echo "Target User DN is:".$target_user_dn."</br>"; echo "Connecting ...</br>"; //创建一个LDAP连接 $ds=ldap_connect($server_host); echo "connect result is " . $ds . "</br>"; if ($ds) { echo "Binding ...</br>"; //绑定连接,如果用户唯一辨识符和密码不正确,将无法绑定 $r=ldap_bind($ds,$manager_user_dn,$user_password); echo "Bind result is " . $r . "</br>"; echo "目标用户: " . $target_user_dn . "</br>"; $result = ldap_add($ds,$target_user_dn,$userdata); if($result===true){ echo "添加成功!</br>"; }else{ echo "添加失败!</br>"; var_dump($result); echo "</br>"; } $target_pass='"password"'; $newPass = iconv( 'UTF-8', 'UTF-16LE', $target_pass ); //通过重写unicodepwd值来更改密码 $passdata["unicodepwd"] = $newPass; $result = ldap_mod_replace($ds,$target_user_dn,$passdata); if($result===true){ echo "修改密码成功!</br>"; }else{ echo "修改密码失败!</br>"; var_dump($result); echo "</br>"; } $account_right["userAccountControl"] = 66048; $change=ldap_mod_replace($ds,$target_user_dn,$account_right); if($change===true){ echo "添加权限成功!</br>"; }else{ echo "添加权限失败!</br>"; var_dump($change); echo "</br>"; } echo "Closing connection</br>"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
如此一来,账户可以成功添加了。
先不要太高兴,到AD上看看添加的信息怎么样,所有中文的信息全部乱码。这也在意料之中,我PHP环境用的是UTF-8编码,Windows是出了名的GB2313。接下来就是整理字符编码的收尾工作了。写一个字符转码的函数,转到GB2312上去,挨个替换就行了。
最终的代码如下
<?php //LDAPS添加用户测试 //LDAP服务器器地址,这里就是AD域控的地址 $server_host="ldaps://ad.lab.pypy.fun:636"; //用于连接LDAP的管理用户唯一辨识符,也可以使用administrator@sub.domain.com这种格式 $manager_user_dn="CN=Administrator,CN=Users,DC=lab,DC=pypy,DC=fun"; //用户密码 $user_password="password"; //需要添加目标用户 //设要添加的用户为17100001@lab.pypy.fun $User_account='17100001'; $target_user_dn="CN=".$User_account.",CN=Users,DC=lab,DC=pypy,DC=fun"; function to_gb2312($string_in){ $str_encode = iconv( 'UTF-8', 'EUC-CN',$string_in ); return $str_encode; } /** * 表分类信息,保持不变即可 */ $userdata["objectClass"][0] = 'top'; $userdata["objectClass"][1] = 'person'; $userdata["objectClass"][2] = 'organizationalPerson'; $userdata["objectClass"][3] = 'user'; $userdata["instanceType"] = 4; /** * UserPrincipalName:指定要由客户端进行身份验证的服务的用户主体名称 (UPN)。 * 一个用户帐户名(有时称为“用户登录名”) * 和一个域名(标识用户帐户所在的域), * 这是登录到Windows域的标准用法。 * 格式是: xiaowen@azureyun.com (类电子邮件地址)。 * SamAccountName:在AD属性AMAccountName中,存储帐户登录名或用户对象, * 实际上是命名符号“Domain\LogonName ”中使用的旧NetBIOS表单, * 该属性是域用户对象的必需属性;而SAMAccountName应始终与UPN主体名称保持一致, * 即SAMAccountName必须等于属性“UserPrincipalName” 的前缀部分。 * 并且应该与CN保持相同。 * * 格式规定 * 不能超过20个字符,不允许出现\ / [] :; | =,+ *?<> @等特殊字符; */ $userdata["sAMAccountName"] = $User_account; $userdata["userPrincipalName"] = $User_account."@lab.pypy.fun"; /** * name:账户名称,应与sAMAccountName保持一致 */ $userdata["name"] = $User_account; /** * userAccountControl记录了用户的AD账号的很多属性信息,该属性标志是累积性的。 * 也就是各属性相加之和。常见的有以下几种属性: * 禁用账户-----------------2 * 需要主文件夹-------------8 * 不需要密码--------------32 * 不能更改密码-------------64 * 使用可逆加密存储密码-----128 * 默认账户类型------------512 * 密码永不过期----------65536 * * 没有声明该值为514即默认账户类型+禁用账户 * 设为66048即为默认账户类型+密码永不过期 */ // $userdata["userAccountControl"] = 66048; $userdata["displayName"] = to_gb2312('张三');//用户姓名 $userdata["company"] = to_gb2312('公司名称'); $userdata["department"] = to_gb2312('部门名称'); $userdata["physicalDeliveryOfficeName"] = to_gb2312('办公室'); $userdata["title"] = to_gb2312('职务'); $userdata["wWWHomePage"] = 'http://blog.tianjinkun.com';//个人主页 $userdata["mail"] = 'mail@test.com'; $userdata["description"] = to_gb2312('个人描述'); $userdata["telephoneNumber"] = '110'; $userdata["ipPhone"] = '8888888';//IP电话,可以用来存储QQ号码 echo "<h3>LDAPS Add Account test</h3>"; echo "Server Host is:".$server_host."</br>"; echo "Manager User DN is:".$manager_user_dn."</br>"; echo "Target User DN is:".$target_user_dn."</br>"; echo "Connecting ...</br>"; //创建一个LDAP连接 $ds=ldap_connect($server_host); echo "connect result is " . $ds . "</br>"; if ($ds) { echo "Binding ...</br>"; //绑定连接,如果用户唯一辨识符和密码不正确,将无法绑定 $r=ldap_bind($ds,$manager_user_dn,$user_password); echo "Bind result is " . $r . "</br>"; echo "目标用户: " . $target_user_dn . "</br>"; $result = ldap_add($ds,$target_user_dn,$userdata); if($result===true){ echo "添加成功!</br>"; }else{ echo "添加失败!</br>"; var_dump($result); echo "</br>"; } $target_pass='"password"'; $newPass = iconv( 'UTF-8', 'UTF-16LE', $target_pass ); //通过重写unicodepwd值来更改密码 $passdata["unicodepwd"] = $newPass; $result = ldap_mod_replace($ds,$target_user_dn,$passdata); if($result===true){ echo "修改密码成功!</br>"; }else{ echo "修改密码失败!</br>"; var_dump($result); echo "</br>"; } $account_right["userAccountControl"] = 66048; $change=ldap_mod_replace($ds,$target_user_dn,$account_right); if($change===true){ echo "添加权限成功!</br>"; }else{ echo "添加权限失败!</br>"; var_dump($change); echo "</br>"; } echo "Closing connection</br>"; ldap_close($ds); } else { echo "<h4>Unable to connect to LDAP server</h4>"; } ?>
到域控上面查看信息,正常:
随意找一台加入了域的计算机登陆,成功。
至此,所有和PHP网站账户系统和AD域控统一账户,统一登陆的相关测试就全部完成了。待疫情结束,返校之后修改网站相关代码就可以了。
对于网站已有的老用户来说,在登陆时判断一下他的账户,引导他再设置一次密码,迁移相关信息升级成域账号即可。新用户直接注册成为域账户即可。
-
« 上一篇:
【Linux On Web】- 线上体验Linux
发表于 2023-12-13 下午 12:00:42
你好,修改密码成功后,登录失败,报错“此工作站和主域间的信任关系失败”,大佬知道什么原因吗。
发表于 2023-12-15 下午 02:01:38
用本地账户登录,重新加入域控
发表于 2023-12-15 下午 04:25:31
感谢