You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何识别PHP+SendGrid群发邮件中To收件人点击验证按钮?

问题描述

我用PHP结合SendGrid API发送邮件,收件人(To)是单个用户,抄送(Cc)是一组用户,邮件内包含确认按钮并附带token(也可使用JWT token)。但当前token对所有收件人相同,请问怎么确认是To字段的邮箱用户点击了该确认按钮?

以下是我使用的示例代码:

$tokenValue = 'AnyRandomValueorJWTTokenGoesHere';
$mailContent = '<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
      <a href="localhost:3000/mailVerify.php?myToken='.$tokenValue.'" 
    class="button" > Verify </a>
    <script src="script.js"></script>
  </body>
</html>';
$email = new \SendGrid\Mail\Mail();
$email->setFrom("test@example.com", "Example User");
$email->setSubject("Sending with Twilio SendGrid is Fun");
$email->addTo("test@example.com", "Example User");
foreach ($cc as $email) {
    $email->addCc($email, explode("@",$email)[0]);
}
$email->addContent("text/html", $mailContent);
$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
try {
    $response = $sendgrid->send($email);
    print $response->statusCode() . "\n";
    print_r($response->headers());
    print $response->body() . "\n";
} catch (Exception $e) {
    echo 'Caught exception: '. $e->getMessage() ."\n";
}
解决方案

1. 用JWT嵌入收件人身份信息

最可靠的方式是把To字段的邮箱地址作为JWT的claim(比如sub或自定义字段),验证token时直接提取邮箱,判断是否为目标收件人。

步骤:

  • 生成JWT时,将To用户的邮箱加入payload
  • 邮件确认链接使用该JWT作为token
  • 在验证接口解析JWT并校验邮箱是否匹配目标To地址

修改后的代码示例:

先安装JWT库(以firebase/php-jwt为例):

composer require firebase/php-jwt

生成JWT并发送邮件的代码:

use Firebase\JWT\JWT;

$secretKey = 'your_jwt_secret_key'; // 建议存入环境变量,保证安全
$toEmail = "test@example.com"; // To字段的目标邮箱
$payload = [
    'iss' => 'your_app_name',
    'sub' => $toEmail, // 将目标邮箱放入JWT的sub字段
    'exp' => time() + 3600 * 24, // Token有效期24小时
];
$tokenValue = JWT::encode($payload, $secretKey, 'HS256');

$mailContent = '<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
      <a href="localhost:3000/mailVerify.php?myToken='.$tokenValue.'" 
    class="button" > Verify </a>
    <script src="script.js"></script>
  </body>
</html>';

$email = new \SendGrid\Mail\Mail();
$email->setFrom("test@example.com", "Example User");
$email->setSubject("Sending with Twilio SendGrid is Fun");
$email->addTo($toEmail, "Example User");
foreach ($cc as $ccEmail) {
    $email->addCc($ccEmail, explode("@",$ccEmail)[0]);
}
$email->addContent("text/html", $mailContent);
$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
try {
    $response = $sendgrid->send($email);
    print $response->statusCode() . "\n";
    print_r($response->headers());
    print $response->body() . "\n";
} catch (Exception $e) {
    echo 'Caught exception: '. $e->getMessage() ."\n";
}

验证接口mailVerify.php的代码:

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$secretKey = 'your_jwt_secret_key';
$targetToEmail = "test@example.com"; // 预存的目标To邮箱

if (!isset($_GET['myToken'])) {
    die("无效请求");
}

try {
    $decoded = JWT::decode($_GET['myToken'], new Key($secretKey, 'HS256'));
    $clickedEmail = $decoded->sub;
    
    if ($clickedEmail === $targetToEmail) {
        // 确认是To字段用户点击,执行验证逻辑
        echo "验证成功!";
    } else {
        // 非目标用户点击,拒绝操作
        echo "您无权限执行此操作";
    }
} catch (Exception $e) {
    die("无效或过期的token");
}

2. 给Cc用户隐藏/禁用确认按钮(可选优化)

因为Cc用户无需执行确认操作,可通过SendGrid动态模板给不同收件人展示不同内容:

  • 创建SendGrid动态模板,用模板语法判断收件人是否为To用户,仅给To用户显示按钮
  • 发送邮件时给每个收件人传入专属标识(比如is_target_recipient)

示例代码(使用动态模板):

$toEmail = "test@example.com";
$email = new \SendGrid\Mail\Mail();
$email->setFrom("test@example.com", "Example User");
$email->setSubject("Sending with Twilio SendGrid is Fun");
$email->addTo($toEmail, "Example User");
// 给To用户传入标识
$email->addDynamicTemplateData('is_target_recipient', true);
$email->addDynamicTemplateData('verify_token', $tokenValue);

foreach ($cc as $ccEmail) {
    $personalization = new \SendGrid\Mail\Personalization();
    $personalization->addCc(new \SendGrid\Mail\Cc($ccEmail, explode("@",$ccEmail)[0]));
    // 给Cc用户传入标识
    $personalization->addDynamicTemplateData('is_target_recipient', false);
    $email->addPersonalization($personalization);
}

// 设置模板ID
$email->setTemplateId('your_sendgrid_template_id');

$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
// ... 发送逻辑和之前一致

动态模板内容示例(Handlebars语法):

{{#if is_target_recipient}}
    <a href="localhost:3000/mailVerify.php?myToken={{verify_token}}" class="button">Verify</a>
{{else}}
    <p>此邮件为抄送,无需操作</p>
{{/if}}

3. 额外校验(补充方案)

如果不想用JWT,可在验证时让用户输入邮箱,对比是否为To字段的邮箱,但这种方式体验较差,仅作为备选。


内容的提问来源于stack exchange,提问作者Shubham Chaudhary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 18:05:28