1. 生成唯一的重置令牌

步骤
  1. 用户请求重置密码:用户在网站上点击“忘记密码”按钮,输入他们的电子邮件地址。
  2. 生成唯一令牌:服务器生成一个唯一的重置令牌,并将其与用户的电子邮件地址关联起来。
  3. 保存令牌到数据库:将生成的令牌和过期时间保存到数据库中。
  4. 发送重置链接:将包含重置令牌的链接通过电子邮件发送给用户。
底层原理
  • 唯一令牌:使用 bin2hex(random_bytes(32)) 生成一个 64 字节的唯一令牌。
  • 过期时间:计算当前时间加上 10 小时的时间戳,作为令牌的过期时间。
  • 数据库存储:将用户的电子邮件地址、生成的令牌和过期时间保存到数据库中。

2. 用户点击重置链接

步骤
  1. 用户点击链接:用户收到电子邮件后,点击重置链接,链接中包含生成的令牌。
  2. 验证令牌:服务器接收请求,验证令牌是否有效(即是否存在且未过期)。
  3. 重置密码:如果令牌有效,允许用户输入新密码并保存到数据库中。
  4. 清理令牌:重置密码成功后,删除或标记令牌为已使用,防止再次使用。
底层原理
  • URL 构造:重置链接的 URL 形式为 https://example.com/reset-password?token=unique-token。
  • 令牌验证:从数据库中查询令牌及其过期时间,验证令牌是否有效。
  • 密码更新:使用 password_hash 函数对新密码进行哈希处理,并更新数据库中的用户密码。
  • 令牌清理:更新数据库,将已使用的令牌标记为已使用或删除。

3. 具体实现

1. 生成唯一令牌并发送邮件
public function sendResetLink($email) {
    // 生成唯一令牌
    $token = bin2hex(random_bytes(32));
    
    // 计算过期时间(10小时后)
    $expires_at = time() + 10 * 60 * 60;
    
    // 保存令牌到数据库
    $this->db->query("INSERT INTO password_resets (email, token, expires_at) VALUES (?, ?, ?)", [$email, $token, $expires_at]);
    
    // 构造重置链接
    $reset_url = "https://example.com/reset-password?token=" . urlencode($token);
    
    // 发送邮件
    $this->sendEmail($email, "重置密码", "请点击以下链接重置您的密码: " . $reset_url);
}

private function sendEmail($to, $subject, $message) {
    // 使用邮件发送库(如 PHPMailer 或 SwiftMailer)发送邮件
    mail($to, $subject, $message);
}
2. 验证令牌并显示重置密码表单
public function showResetForm($token) {
    // 验证令牌
    $result = $this->db->query("SELECT * FROM password_resets WHERE token = ? AND expires_at > ?", [$token, time()]);
    $row = $result->fetch_assoc();
    
    if (!$row) {
        die("无效或已过期的重置链接");
    }
    
    // 显示重置密码表单
    echo "<form action='/reset-password' method='post'>";
    echo "<input type='hidden' name='token' value='" . htmlspecialchars($token) . "'>";
    echo "<label for='new_password'>新密码:</label>";
    echo "<input type='password' name='new_password'>";
    echo "<button type='submit'>重置密码</button>";
    echo "</form>";
}
3. 处理重置密码请求
public function resetPassword($token, $new_password) {
    // 验证令牌
    $result = $this->db->query("SELECT * FROM password_resets WHERE token = ? AND expires_at > ?", [$token, time()]);
    $row = $result->fetch_assoc();
    
    if (!$row) {
        die("无效或已过期的重置链接");
    }
    
    // 哈希新密码
    $hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
    
    // 更新用户密码
    $this->db->query("UPDATE users SET password = ? WHERE email = ?", [$hashed_password, $row['email']]);
    
    // 清理令牌
    $this->db->query("DELETE FROM password_resets WHERE token = ?", [$token]);
    
    echo "密码已成功重置";
}

4. 数据库表结构

CREATE TABLE password_resets (
    id INT AUTO_INCREMENT PRIMARY KEY,
    email VARCHAR(255) NOT NULL,
    token VARCHAR(255) NOT NULL UNIQUE,
    expires_at INT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

5. 前端表单

<form action="/reset-password" method="post">
    <input type="hidden" name="token" value="<?php echo htmlspecialchars($token); ?>">
    <label for="new_password">新密码:</label>
    <input type="password" name="new_password" required>
    <button type="submit">重置密码</button>
</form>

总结

通过上述步骤,你可以实现一个安全的密码重置功能,确保用户可以通过点击邮箱中的链接在 10 小时内重置密码。关键点包括生成唯一令牌、保存令牌到数据库、发送重置链接、验证令牌、显示重置表单、处理重置请求和清理令牌。这些步骤共同确保了密码重置过程的安全性和有效性。

更多推荐