外文字符和LDAP。LDAP期望使用哪种编码/字符集?

7
我将使用simplexml_load_string()解析XML,并使用其中的数据通过LDAP更新Active Directory(AD)对象。
示例XML(简化版):
<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>Bìlbö Bággįnš</user>
    <user>Gãńdåłf Thê Gręât</user>
    <user>Śām Wīšë</user>
</users>

我首先运行了一个ldap_search()来找到单个用户,然后继续更改他们的属性。直接将上述值使用LDAP注入AD会导致一些非常混乱的字符出现。
例如:Bìlbö BággįnÅ¡ 我尝试过以下函数,但都没有成功:
utf8_encode($str);
utf8_decode($str);
iconv("UTF-8", "ISO-8859-1//TRANSLIT", $str);
iconv("UTF-8", "ASCII//TRANSLIT", $str);
iconv("UTF-8", "T.61", $str);

理想情况下,我不想进行任何这些字符串转换。UTF-8应该没问题,对吧?!
我还注意到以下情况: 我已经打印出值来查看它们的输出情况。在CLI中curl脚本将显示正确的字符,但是Web浏览器显示与AD相同。
发生了什么?我应该看一些其他内容,例如URL编码吗? 我希望这只是我的一个简单错误。
编辑: 我使用AD admin GUI输入了这些字符,以查看它们的输出情况。我可以通过LDAP正确地读取它们。当在浏览器中时,显示正确的字符。通过CLI curling将显示问号而不是外国字符。将其中一个返回值传递给mb_detect_encoding()将返回UTF-8。 我决定立即通过不写入新字符串,而只是反转现有值并保存对象来修改同一对象。这很好用——我在AD中看到了正确的值(反转)。
  • 在 Mac OS X 10.7 Lion 上开发 - PHP 5.4.3
  • 在 Red Hat 6 上运行生产环境 - PHP 5.4.3
  • AD 服务器:Windows 2003

更新: 几个月后,我无法找到这个问题的答案/解决方案。 最终,我选择将字符替换为它们的非重音等效字符(并不理想,我知道)。


使用 bin2hex() 函数之一来显示二进制字符串值。 - deceze
@deceze bin2hex( á ) == c3a1。我不确定字符在复制和粘贴到各个地方时是否会发生任何变化。系统剪贴板可能会损坏/更改它(?)。 - OmidTahouri
我猜LDAP/AD是LDAP的Microsoft版本,使用WINDOWS-1250编码,因为来自Microsoft的任何东西都是如此... 因此,iconv("UTF-8", "WINDOWS-1250", $str); 应该可以工作。 - shadyyx
它不喜欢WINDOWS-1250 - iconv():在输入字符串中检测到非法字符 - OmidTahouri
这与 PHP 文件本身没有 UTF8 编码有关吗?访问 LDAP 的 PHP 文件应将其编码类型设置为 UTF-8 吗? - user991554
4个回答

11

你在运行LDAP脚本的时候是在NIX盒子上还是Windows盒子上?这是同一台配置了AD的服务器吗?PHP的版本是什么? - Mike Mackintosh
尝试:iconv(“UTF-8”,“T.61”,$str); - Mike Mackintosh
好的,我已经将所请求的信息添加到问题中了。...还有:"错误的字符集,不允许从'UTF-8'转换为'T.61'" :( - OmidTahouri
尝试使用ldap_8859_to_t61()ldap_t61_to_8859。看看它们是否能让你进行转换。 - Mike Mackintosh
这解决了我遇到的问题。虽然在手册中有提到,但我已经忽略了1000次。 - user1457656
接受这个答案,因为它最有可能解决任何其他遇到类似问题的人的困扰。我已经尝试了所有方法,但都没有成功 :( - OmidTahouri

2

以下是适用于我的解决方案。请按照以下步骤操作:

1.) 首先确保您正在使用LDAP协议版本3,默认情况下使用"UTF-8"编码:

ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);

2.) 如果你想要更改用户的密码,那么请确保 "使用TLS" 选项设置为 true ,并将使用SSL设置为 false

ldap_start_tls($ldapConnection);

3.) 我使用的端口号是389

4.) 使用PHP函数ldap_mod_replace来替换用户密码。

5.) 使用以下函数对你的$password进行编码:

public function encodePassword($password)
{
    $password="\"".$password."\"";
    $encoded="";
    for ($i=0; $i <strlen($password); $i++){ 
        $encoded.="{$password{$i}}\000"; 
    }
    return $encoded;
}

6.) 使用以下逻辑更改用户密码:

$password="test";
if(mb_detect_encoding($password) == 'UTF-8')
{
    $password = utf8_decode($password);
}

$add=array();
$add["unicodePwd"][0] = encodePassword($password);

$result = @ldap_mod_replace($ldapConnection, $userDn, $add);
if ($result === false){
    //your action
}
else{
    //Your action
}

请注意,函数encodePassword将把您的$password编码为UTF-8编码。如果您的密码已经是UTF-8编码,则在发送到encodePassword函数之前必须对其进行解码。这就是我写这行代码的原因:
if(mb_detect_encoding($password) == 'UTF-8')
{
    $password = utf8_decode($password);
}

当我在密码中提供德语umlauts:äüößÄÜ等时,此代码对我有效。


设置这个完全解决了我的问题!!谢谢 - brunobliss

1

对于那些遇到困难的人,还有一点需要提醒:

如果你的文本已经是UTF-8编码,请不要尝试重新编码。请注意utf8_encode文档页面上的以下注释。重新编码已经编码过的字符串会导致乱码。此外,该函数只允许从一种特定的编码转换为另一种。

您可以轻松测试是否需要将字符串编码为UTF-8,例如执行以下操作:

if (!preg_match('//u', $value)) {
    // do your encoding process...
}

关于网页中字符显示不正确,但在CLI上则正常,确保你在header中设置了正确的字符集:
header('Content-type: text/html; charset=utf-8');

1
我已经成功地通过两个步骤在LDAP中添加了外语字符:
  • 只使用ASCII字符添加用户(iconv "ASCII//TRANSLIT")

  • 使用ldapmodify更新字段中的UTF-8字符

LDAPv3是UTF-8编码的,但我使用的工具(来自smbldap-tools)没有正确处理它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接