Files
SunShineMusic/forgot_password.php
2025-09-24 14:15:03 +00:00

432 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// 强制显示所有错误(仅用于调试)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// 记录错误到日志文件
ini_set('log_errors', 1);
ini_set('error_log', 'error_log.txt');
try {
// 启动会话
session_start();
// 检查用户是否已登录,如果已登录则重定向到首页
if (isset($_SESSION['user_logged_in']) && $_SESSION['user_logged_in'] === true) {
header('Location: index.php');
exit;
}
// --- 配置区 ---
// 数据库连接配置
$servername = "localhost";
$dbusername = "a1sax1m9i";
$dbpassword = "a1sax1m9i";
$dbname = "a1sax1m9i";
// QQ邮箱配置 - 请修改为你的信息
$smtpHost = 'smtp.qq.com'; // QQ邮箱SMTP服务器
$smtpPort = 465; // QQ邮箱SMTP端口(SSL)
$smtpUsername = 'luoriguodu@qq.com'; // 你的QQ邮箱地址
$smtpPassword = 'awjoclnnhqpybiie'; // QQ邮箱授权码(不是登录密码)
$fromName = "音乐分享网站"; // 发件人名称
// 初始化消息变量
$message = "";
$messageType = ""; // success 或 error
// 确保密码重置表存在
function ensurePasswordResetTableExists($conn) {
$sql = "CREATE TABLE IF NOT EXISTS password_reset_tokens (
user_id INT NOT NULL,
token VARCHAR(255) NOT NULL,
expiry DATETIME NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
if (!$conn->query($sql)) {
return "创建密码重置表失败: " . $conn->error;
}
return true;
}
// 生成随机令牌
function generateToken() {
return bin2hex(random_bytes(32));
}
// 使用QQ SMTP发送密码重置邮件
function sendResetEmailViaQQSMTP($toEmail, $resetLink, $smtpHost, $smtpPort, $smtpUsername, $smtpPassword, $fromName) {
// 邮件主题
$subject = "=?UTF-8?B?" . base64_encode("密码重置请求 - 音乐分享网站") . "?=";
// 邮件内容
$message = '
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>密码重置请求 - 音乐分享网站</title>
<!-- 基础样式重置,适配不同邮箱客户端 -->
<style type="text/css">
/* 清除默认边距和内边距 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", "Arial", sans-serif;
}
/* 响应式图片 */
img {
max-width: 100%;
height: auto;
border: none;
}
/* 链接样式统一 */
a {
text-decoration: none;
transition: all 0.3s ease;
}
/* 按钮基础样式 */
.btn {
display: inline-block;
padding: 12px 24px;
background-color: #2c3e50;
color: #ffffff !important;
font-size: 16px;
font-weight: 500;
border-radius: 6px;
text-align: center;
min-width: 180px;
}
.btn:hover {
background-color: #34495e;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}
/* 内容容器样式 */
.email-container {
max-width: 600px;
margin: 0 auto;
padding: 20px 15px;
background-color: #f9fafb;
}
/* 邮件头部样式 */
.email-header {
padding: 15px 0;
margin-bottom: 25px;
border-bottom: 1px solid #eee;
}
.header-logo {
display: flex;
align-items: center;
gap: 10px;
color: #2c3e50;
}
.logo-icon {
font-size: 28px;
color: #2c3e50;
}
.logo-text {
font-size: 20px;
font-weight: 600;
}
/* 邮件主体样式 */
.email-body {
padding: 30px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
}
.body-title {
font-size: 22px;
color: #2c3e50;
margin-bottom: 18px;
font-weight: 600;
}
.body-desc {
font-size: 15px;
color: #666666;
line-height: 1.6;
margin-bottom: 25px;
}
.body-desc p {
margin-bottom: 12px;
}
/* 按钮容器,确保居中 */
.btn-container {
margin: 30px 0;
text-align: center;
}
/* 邮件底部样式 */
.email-footer {
margin-top: 25px;
padding-top: 15px;
border-top: 1px solid #eee;
font-size: 13px;
color: #999999;
text-align: center;
line-height: 1.5;
}
.footer-note {
margin-top: 8px;
}
/* 适配移动设备 */
@media (max-width: 480px) {
.email-body {
padding: 20px 15px;
}
.body-title {
font-size: 19px;
}
.body-desc {
font-size: 14px;
}
.btn {
padding: 10px 20px;
font-size: 14px;
min-width: 150px;
}
}
</style>
<!-- 引入Font Awesome图标若邮箱客户端支持增强视觉效果 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<!-- 邮件整体容器,控制最大宽度和背景 -->
<div class="email-container">
<!-- 邮件头部:显示网站标识 -->
<div class="email-header">
<div class="header-logo">
<i class="fas fa-music logo-icon"></i>
<span class="logo-text">音乐分享网站</span>
</div>
</div>
<!-- 邮件主体:核心内容区域 -->
<div class="email-body">
<h1 class="body-title">您请求重置密码</h1>
<div class="body-desc">
<p>您好!我们收到了您的密码重置请求,若此操作非您本人发起,请忽略此邮件。</p>
<p>请点击下方按钮重置您的密码链接有效期为1小时过期后需重新申请</p>
</div>
<!-- 重置按钮:居中显示,突出引导 -->
<div class="btn-container">
<a href="' . htmlspecialchars($resetLink) . '" class="btn">
<i class="fas fa-key mr-2"></i>立即重置密码
</a>
</div>
<div class="btn-container">
<a href="mailto:luoriguodu@foxmail.com" class="btn">
<i class="fas fa-key mr-2"></i>反馈问题
</a>
</div>
<!-- 备用链接:防止按钮失效时用户仍能操作 -->
<div class="body-desc">
<p>若按钮无法点击,可将下方链接复制到浏览器地址栏访问:</p>
<p style="word-break: break-all; color: #3498db;">
' . htmlspecialchars($resetLink) . '
</p>
</div>
</div>
<!-- 邮件底部:补充说明和版权信息 -->
<div class="email-footer">
<p>此邮件为系统自动发送,请勿直接回复</p>
<p class="footer-note">© 2024 音乐分享网站 版权所有 | 如有疑问,请联系客服</p>
</div>
</div>
</body>
</html>
';
// 创建邮件头部
$headers = [
'From' => $fromName . " <" . $smtpUsername . ">",
'Reply-To' => $smtpUsername,
'X-Mailer' => 'PHP/' . phpversion(),
'MIME-Version' => '1.0',
'Content-Type' => 'text/html; charset=UTF-8'
];
// 组装头部字符串
$headerString = "";
foreach ($headers as $key => $value) {
$headerString .= $key . ": " . $value . "\r\n";
}
// 使用stream_socket_client发送邮件
$socket = stream_socket_client(
'ssl://' . $smtpHost . ':' . $smtpPort,
$errorNumber,
$errorMessage,
30,
STREAM_CLIENT_CONNECT
);
if (!$socket) {
throw new Exception("无法连接到SMTP服务器: $errorMessage ($errorNumber)");
}
// 发送SMTP命令
fwrite($socket, "EHLO " . gethostname() . "\r\n");
fread($socket, 1024);
fwrite($socket, "AUTH LOGIN\r\n");
fread($socket, 1024);
fwrite($socket, base64_encode($smtpUsername) . "\r\n");
fread($socket, 1024);
fwrite($socket, base64_encode($smtpPassword) . "\r\n");
fread($socket, 1024);
fwrite($socket, "MAIL FROM: <" . $smtpUsername . ">\r\n");
fread($socket, 1024);
fwrite($socket, "RCPT TO: <" . $toEmail . ">\r\n");
fread($socket, 1024);
fwrite($socket, "DATA\r\n");
fread($socket, 1024);
fwrite($socket, "Subject: " . $subject . "\r\n");
fwrite($socket, $headerString . "\r\n");
fwrite($socket, $message . "\r\n");
fwrite($socket, ".\r\n");
fread($socket, 1024);
fwrite($socket, "QUIT\r\n");
fread($socket, 1024);
fclose($socket);
return true;
}
// 处理表单提交
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST['email'] ?? '');
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$message = "请输入有效的邮箱地址";
$messageType = "error";
} else {
$conn = new mysqli($servername, $dbusername, $dbpassword, $dbname);
if ($conn->connect_error) {
throw new Exception("数据库连接失败: " . $conn->connect_error);
}
$tableCheck = ensurePasswordResetTableExists($conn);
if ($tableCheck !== true) {
throw new Exception($tableCheck);
}
$stmt = $conn->prepare("SELECT id FROM users WHERE email = ?");
if (!$stmt) {
throw new Exception("准备查询语句失败: " . $conn->error);
}
$stmt->bind_param("s", $email);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == 1) {
$stmt->bind_result($userId);
$stmt->fetch();
$token = generateToken();
$expiry = date('Y-m-d H:i:s', strtotime('+1 hour'));
$updateStmt = $conn->prepare("INSERT INTO password_reset_tokens (user_id, token, expiry) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE token = ?, expiry = ?");
if (!$updateStmt) {
throw new Exception("准备更新语句失败: " . $conn->error);
}
$updateStmt->bind_param("issss", $userId, $token, $expiry, $token, $expiry);
$updateStmt->execute();
// 生成重置链接
$resetLink = "http://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "/reset_password.php?token=" . $token;
try {
// 发送重置邮件
sendResetEmailViaQQSMTP(
$email,
$resetLink,
$smtpHost,
$smtpPort,
$smtpUsername,
$smtpPassword,
$fromName
);
$message = "密码重置链接已发送到您的邮箱请查收1小时内有效";
$messageType = "success";
} catch (Exception $e) {
$message = "邮件发送失败: " . $e->getMessage();
$messageType = "error";
error_log("邮件发送错误: " . $e->getMessage());
}
$updateStmt->close();
} else {
// 安全考虑,无论邮箱是否存在,都显示相同的成功消息
$message = "如果该邮箱已注册,密码重置链接将发送";
$messageType = "success";
}
$stmt->close();
$conn->close();
}
}
} catch (Exception $e) {
$message = "系统错误: " . $e->getMessage();
$messageType = "error";
error_log("Forgot Password Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>忘记密码 - 音乐分享网站</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body { font-family: 'Arial', sans-serif; max-width: 500px; margin: 2rem auto; padding: 0 1rem; background-color: #f5f5f7; }
.container { background-color: white; padding: 2rem; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #2c3e50; }
.form-group { margin-bottom: 1.5rem; }
label { display: block; margin-bottom: 0.5rem; color: #333; }
input { width: 100%; padding: 0.8rem; border: 1px solid #ddd; border-radius: 5px; box-sizing: border-box; font-size: 1rem; }
button { width: 100%; padding: 0.8rem; background-color: #2c3e50; color: white; border: none; border-radius: 5px; font-size: 1rem; cursor: pointer; }
.message { padding: 1rem; border-radius: 5px; margin-bottom: 1.5rem; }
.error-message { background-color: #f8d7da; color: #721c24; }
.success-message { background-color: #d4edda; color: #155724; }
.back-link { text-align: center; margin-top: 1.5rem; }
.back-link a { color: #2c3e50; text-decoration: none; }
</style>
</head>
<body>
<div class="container">
<h1><i class="fas fa-key"></i> 忘记密码</h1>
<?php if (!empty($message)): ?>
<div class="message <?php echo $messageType === 'error' ? 'error-message' : 'success-message'; ?>">
<?php echo $message; ?>
</div>
<?php endif; ?>
<form method="post" action="">
<div class="form-group">
<label for="email">请输入您注册时使用的邮箱</label>
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>" required>
</div>
<button type="submit">获取重置链接</button>
</form>
<div class="back-link">
<a href="login.php"><i class="fas fa-arrow-left"></i> 返回登录</a>
</div>
</div>
</body>
</html>