PHPMailer 6.9 如何用 XCLIENT 扩展属性向 Postfix 网关传递转发身份
【免费下载链接】PHPMailerThe classic email sending library for PHP项目地址: https://gitcode.com/GitHub_Trending/ph/PHPMailer
当你的 PHP 应用不直接连外网邮件服务器,而是先把信交给内网的 Postfix 网关,再由网关转发出去时,网关只知道连接来自哪台机器,却不知道真实用户的登录身份。Postfix 的 XCLIENT 扩展就是解决这个的:客户端在 SMTP 会话中发送一条XCLIENT命令,把LOGIN、ADDR、HELO等属性传递给网关。PHPMailer 6.9.0 起提供了setSMTPXclientAttribute和getSMTPXclientAttributes两个方法支持这个扩展(见 changelog.md),仓库当前版本为 6.9.3。这篇文章只讲一件事:如何用这两个方法把转发身份传给 Postfix 网关,并确认命令确实发出去了。
前提条件
composer.json 声明的最低要求是 PHP>=5.5.0,以及ext-ctype、ext-filter、ext-hash三个扩展。安装库:
composer require phpmailer/phpmailer网关侧需要 Postfix 已配置好smtpd_client_restrictions中的 XCLIENT 支持,并且允许来自你这台客户端的 XCLIENT 属性——这是 XCLIENT 机制本身的前提,PHPMailer 这边无法替代。PHPMailer 源码注释明确指出:允许的 XCLIENT 属性由 SMTP 服务器决定,PHPMailer 不会检查 EHLO 响应(见 src/SMTP.php)。
允许的 XCLIENT 属性
SMTP类中维护了一个白名单 SMTP::$xclient_allowed_attributes,只有这 8 个属性名可以被设置和发送:
NAME, ADDR, PORT, PROTO, HELO, LOGIN, DESTADDR, DESTPORT典型转发场景用的是LOGIN(真实登录名)、ADDR(客户端真实 IP)、HELO(客户端 HELO 域名)。
设置 XCLIENT 属性并发送
参考 examples/smtp.phps 中的用法(示例中这几行以注释形式给出,取消注释并替换为你自己的值即可)。下面是一个最小可用脚本,ADDR、LOGIN、HELO的值需要替换为你转发链路上的真实值:
<?php use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; date_default_timezone_set('Etc/UTC'); require 'vendor/autoload.php'; $mail = new PHPMailer(); $mail->isSMTP(); // 打开调试,DEBUG_SERVER 同时显示客户端和服务端消息 $mail->SMTPDebug = SMTP::DEBUG_SERVER; $mail->Host = 'mail.example.com'; // 你的 Postfix 网关地址 $mail->Port = 25; // SMTP XCLIENT 属性 $mail->setSMTPXclientAttribute('ADDR', '10.10.10.10'); // 客户端真实 IP $mail->setSMTPXclientAttribute('LOGIN', 'user@example.com'); // 真实登录身份 $mail->setSMTPXclientAttribute('HELO', 'client.example.com'); if (!$mail->send()) { echo 'Mailer Error: ' . $mail->ErrorInfo; } else { echo 'Message sent!'; }属性的增删改规则由 setSMTPXclientAttribute 实现:
- 属性名不在 8 项白名单内时,方法直接返回
false,不会保存; - 传入非
null值时保存或覆盖该属性,返回true; - 对已存在的属性传入
null表示删除该属性。
发送时 XCLIENT 命令如何发出
在 PHPMailer::smtpSend 中,一旦设置了任何 XCLIENT 属性,send()会先建立 SMTP 连接,然后在MAIL FROM之前调用$this->smtp->xclient($this->SMTPXClient)。
SMTP::xclient() 把属性拼成XCLIENT ADDR=... LOGIN=...这样的命令发送给服务器,并期望返回码 250;服务器返回其他码时该命令返回false。也就是说,如果网关没有放行你的 XCLIENT 属性,发信流程会在这里失败,send()抛出异常或返回false,$mail->ErrorInfo中能看到错误详情。
验证属性是否生效
仓库测试 testSmtpXclient 展示了完整的核对方式,可以照搬做本地验证:
$mail->isSMTP(); $mail->SMTPAuth = false; $mail->setSMTPXclientAttribute('ADDR', '127.0.0.1'); $mail->setSMTPXclientAttribute('LOGIN', 'user@example.com'); $mail->setSMTPXclientAttribute('HELO', 'test.example.com'); // 白名单外的属性名返回 false var_dump($mail->setSMTPXclientAttribute('INVALID', 'value')); // false // 读取当前保存的全部 XCLIENT 属性 var_dump($mail->getSMTPXclientAttributes()); // ['ADDR' => '127.0.0.1', 'LOGIN' => 'user@example.com', 'HELO' => 'test.example.com'] // 传 null 删除属性 $mail->setSMTPXclientAttribute('HELO', null); var_dump($mail->getSMTPXclientAttributes()); // ['ADDR' => '127.0.0.1', 'LOGIN' => 'user@example.com']运行时的另一条验证路径是把SMTPDebug设为SMTP::DEBUG_SERVER:调试输出会同时显示客户端发往服务器的消息,可以直接看到XCLIENT ADDR=... LOGIN=...命令和服务器给出的应答码。测试中在preSend()后调用send()并断言返回true,即为整条链路(含 XCLIENT 命令)通过。
限制与边界
- PHPMailer 不解析 EHLO 响应来判断服务器是否支持 XCLIENT,属性是否被接受完全取决于服务器对
XCLIENT命令的应答(250 才算成功); - 只有 8 个白名单属性可用,其余名字一律被
setSMTPXclientAttribute拒绝; - 未设置任何 XCLIENT 属性时,
xclient()直接返回true,不会发送任何命令——所以"没报错"不等于命令已发出,需要靠getSMTPXclientAttributes()或DEBUG_SERVER输出确认。
【免费下载链接】PHPMailerThe classic email sending library for PHP项目地址: https://gitcode.com/GitHub_Trending/ph/PHPMailer
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考