找回密码OTP邮件发送报错求助:收件人邮箱未设置或为空
问题描述
开发支持邮箱获取OTP的找回密码表单时,出现报错:Message could not be sent. Mailer Error: Recipient email address is not set or empty。需求为用户提交表单后,填写的邮箱能收到包含OTP及跳转至OTP验证页面链接的邮件。
错误原因分析
- 页面逻辑冲突:原代码将PHP邮件发送逻辑与HTML表单放在同一文件,页面加载时PHP代码就会执行,此时未提交表单,
$_POST['email']为空,触发收件人不存在的异常。 - 表单提交目标混乱:HTML表单
action指向otp-confirmation.php,但AJAX请求目标是当前文件,导致流程错位。 - 邮件配置缺失:未设置发件人信息,部分SMTP服务器会拒绝发送;邮件仅包含OTP,未添加验证页面跳转链接。
- 流程设计不合理:找回密码流程应分“提交邮箱→发送OTP→验证OTP并重置密码”三步,原表单同时包含邮箱和新密码字段,逻辑混乱。
修复方案
1. 拆分页面职责
将流程拆分为三个独立页面,明确各页面功能:
forgot-password.php:展示邮箱输入表单,提交后触发AJAX请求发送OTP。send-otp.php:处理AJAX请求,生成OTP并发送邮件,返回JSON格式结果。otp-confirmation.php:展示OTP验证表单,验证通过后允许用户重置密码。
2. 修正PHPMailer配置
- 添加发件人信息(
setFrom),确保SMTP服务器认可。 - 改为HTML格式邮件,插入验证页面跳转链接。
- 仅在AJAX请求触发时执行邮件发送逻辑,避免页面加载时误触发。
3. 完善会话与安全机制
- 存储收件邮箱和OTP到会话,用于后续验证。
- 给OTP设置5分钟过期时间,提升安全性。
完整修复代码
1. forgot-password.php(邮箱提交页面)
<?php session_start(); ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>重置密码 - Obaju</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row justify-content-center"> <div class="col-md-6"> <div class="card mt-5"> <div class="card-header">重置密码</div> <div class="card-body"> <form id="forgot-password-form"> <div class="form-group"> <label for="email">邮箱地址</label> <input type="email" class="form-control" id="email" name="email" required> </div> <button type="submit" class="btn btn-primary">发送验证码</button> </form> <div id="message" class="mt-3"></div> </div> </div> </div> </div> </div> <script src="vendor/jquery/jquery.min.js"></script> <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script> <script> $(document).ready(function(){ $("#forgot-password-form").submit(function(event){ event.preventDefault(); var formData = $(this).serialize(); $.ajax({ url: "send-otp.php", type: "POST", data: formData, success: function(response){ var res = JSON.parse(response); if(res.status === "success"){ $("#message").html('<div class="alert alert-success">验证码已发送至您的邮箱,请查收</div>'); setTimeout(function(){ window.location.href = "otp-confirmation.php?email=" + res.email; }, 2000); } else { $("#message").html('<div class="alert alert-danger">' + res.message + '</div>'); } }, error: function(){ $("#message").html('<div class="alert alert-danger">请求失败,请稍后重试</div>'); } }); }); }); </script> </body> </html>
2. send-otp.php(OTP发送处理)
<?php session_start(); $phpMailerDir = 'C:\wamp64\www\obaju\distribution\sendEmails\vendor\phpmailer\phpmailer'; require_once $phpMailerDir . '\src\PHPMailer.php'; require_once $phpMailerDir . '\src\SMTP.php'; require_once $phpMailerDir . '\src\Exception.php'; use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\SMTP; use PHPMailer\PHPMailer\Exception; header('Content-Type: application/json'); $response = ['status' => 'error', 'message' => '未知错误']; if(isset($_POST['email']) && !empty($_POST['email'])){ $recipient_email = trim($_POST['email']); $_SESSION['reset_email'] = $recipient_email; try { $mail = new PHPMailer(true); // SMTP服务器配置 $mail->isSMTP(); $mail->Host = 'smtp-mail.outlook.com'; $mail->SMTPAuth = true; $mail->Username = "gcira2023@outlook.com"; $mail->Password = "***"; // 替换为实际SMTP密码 $mail->SMTPSecure = 'tls'; $mail->Port = 587; // 发件人设置 $mail->setFrom('gcira2023@outlook.com', 'Obaju商城'); $mail->addAddress($recipient_email); // 生成OTP并存储会话 $otp = rand(100000, 999999); $_SESSION['otp'] = $otp; $_SESSION['otp_expire'] = time() + 300; // 5分钟过期 // 构建验证链接 $verify_link = "http://" . $_SERVER['HTTP_HOST'] . "/obaju/distribution/otp-confirmation.php?email=" . urlencode($recipient_email); // HTML格式邮件内容 $mail->isHTML(true); $mail->Subject = 'Obaju商城密码重置验证码'; $mail->Body = <<<HTML <p>您好,您正在申请重置Obaju商城账号密码,请使用以下验证码完成验证:</p> <p><strong>$otp</strong></p> <p>或者点击下方链接直接跳转至验证页面:</p> <p><a href="$verify_link">点击验证</a></p> <p>验证码5分钟内有效,如非本人操作请忽略此邮件。</p> HTML; $mail->send(); $response = ['status' => 'success', 'message' => '邮件发送成功', 'email' => $recipient_email]; } catch (Exception $e) { $response['message'] = '邮件发送失败:' . $e->getMessage(); } } else { $response['message'] = '请填写有效的邮箱地址'; } echo json_encode($response); ?>
3. otp-confirmation.php(OTP验证及密码重置页面)
<?php session_start(); // 检查会话有效性 if(!isset($_SESSION['reset_email']) || !isset($_SESSION['otp']) || $_SESSION['otp_expire'] < time()){ header('Location: forgot-password.php'); exit; } $email = $_SESSION['reset_email']; $error = ''; if($_SERVER['REQUEST_METHOD'] === 'POST'){ $user_otp = trim($_POST['otp']); $new_password = trim($_POST['new-password']); $confirm_password = trim($_POST['confirm-password']); if($user_otp == $_SESSION['otp']){ if($new_password === $confirm_password && strlen($new_password) >= 6){ // 此处添加更新数据库密码的逻辑 // 示例:update users set password = password_hash($new_password, PASSWORD_DEFAULT) where email = '$email' // 重置会话 unset($_SESSION['otp'], $_SESSION['otp_expire'], $_SESSION['reset_email']); header('Location: dashboard.php'); exit; } else { $error = '两次输入的密码不一致或长度不足6位'; } } else { $error = '验证码错误'; } } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>验证验证码 - Obaju</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css"> </head> <body> <div class="container"> <div class="row justify-content-center"> <div class="col-md-6"> <div class="card mt-5"> <div class="card-header">验证验证码</div> <div class="card-body"> <?php if($error): ?> <div class="alert alert-danger"><?php echo $error; ?></div> <?php endif; ?> <form method="post"> <div class="form-group"> <label for="otp">验证码</label> <input type="text" class="form-control" id="otp" name="otp" required maxlength="6"> </div> <div class="form-group"> <label for="new-password">新密码</label> <input type="password" class="form-control" id="new-password" name="new-password" required minlength="6"> </div> <div class="form-group"> <label for="confirm-password">确认密码</label> <input type="password" class="form-control" id="confirm-password" name="confirm-password" required minlength="6"> </div> <button type="submit" class="btn btn-primary">重置密码</button> </form> </div> </div> </div> </div> </div> <script src="vendor/jquery/jquery.min.js"></script> <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script> </body> </html>
内容的提问来源于stack exchange,提问作者The01800
相关产品推荐
相关产品推荐

