現代找回密碼機制不暴露原密碼(密碼應單向哈希存儲),核心流程:
CREATE TABLE `users` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(100) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) NOT NULL, -- password_hash() 生成
`reset_token` VARCHAR(64) DEFAULT NULL,
`reset_token_expires` DATETIME DEFAULT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
找回密碼
找回密碼
prepare("SELECT id, username FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
echo json_encode(['success' => false, 'message' => '該郵箱未註冊']);
exit;
}
// 生成安全 Token:隨機 32 字節 + 用戶 ID + 時間戳,SHA256
$token = hash('sha256', $user['id'] . $user['username'] . random_bytes(32) . time());
$expires = date('Y-m-d H:i:s', strtotime('+1 hour')); // 1 小時有效期
// 存儲 Token
$stmt = $pdo->prepare("UPDATE users SET reset_token = ?, reset_token_expires = ? WHERE id = ?");
$stmt->execute([$token, $expires, $user['id']]);
// 構建重置鏈接
$resetUrl = "https://{$_SERVER['HTTP_HOST']}/reset.php?token={$token}&email=" . urlencode($email);
// 發送郵件(使用 PHPMailer 或 Symfony Mailer)
$mailResult = sendResetEmail($email, $user['username'], $resetUrl, $expires);
if ($mailResult) {
echo json_encode(['success' => true, 'message' => '重置郵件已發送,請檢查郵箱(含垃圾箱),鏈接 1 小時內有效。']);
} else {
echo json_encode(['success' => false, 'message' => '郵件發送失敗,請聯繫管理員']);
}
function sendResetEmail($to, $username, $url, $expires) {
// 這裡使用 PHPMailer 示例,需 composer require phpmailer/phpmailer
require_once __DIR__ . '/vendor/autoload.php';
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
try {
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@example.com';
$mail->Password = 'your_smtp_password';
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
$mail->CharSet = 'UTF-8';
$mail->setFrom('noreply@example.com', '系統通知');
$mail->addAddress($to, $username);
$mail->Subject = '密碼重置請求';
$mail->isHTML(true);
$mail->Body = "
親愛的 {$username},
您請求重置密碼。請點擊下方鏈接設置新密碼(鏈接將於 {$expires} 失效):
若非您本人操作,請忽略此郵件。
";
$mail->send();
return true;
} catch (Exception $e) {
error_log("郵件發送失敗: {$mail->ErrorInfo}");
return false;
}
}
prepare("SELECT id FROM users WHERE email = ? AND reset_token = ? AND reset_token_expires > NOW()");
$stmt->execute([$email, $token]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
die('鏈接無效或已過期,請重新申請');
}
// 顯示重置表單
?>
重置密碼
設置新密碼
false, 'message' => '參數缺失']);
exit;
}
// 再次驗證 Token
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ? AND reset_token = ? AND reset_token_expires > NOW()");
$stmt->execute([$email, $token]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
echo json_encode(['success' => false, 'message' => '鏈接無效或已過期']);
exit;
}
// 密碼強度檢查(可擴展)
if (strlen($password) < 8) {
echo json_encode(['success' => false, 'message' => '密碼長度至少 8 位']);
exit;
}
// 更新密碼,清除 Token
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("UPDATE users SET password_hash = ?, reset_token = NULL, reset_token_expires = NULL WHERE id = ?");
$stmt->execute([$hash, $user['id']]);
echo json_encode(['success' => true, 'message' => '密碼重置成功!'])
password_hash() + password_verify(),禁用 MD5/SHA1random_bytes() + 哈希,不可預測