在现代Web开发中,构建一个安全可靠的用户登录系统是至关重要的一环,其核心在于PHP如何与数据库进行交互,以验证用户身份,本文将详细拆解这一过程,从数据库设计到PHP代码实现,提供一个清晰、安全的实践指南。

准备工作:设计用户数据表
在编写任何PHP代码之前,我们需要一个数据库来存储用户信息,一个简单而有效的用户表通常包含id、username和password字段。
重要提示:绝对不能明文存储用户密码,必须使用PHP的password_hash()函数对密码进行哈希处理,以增强安全性。
以下是一个创建users表的SQL语句示例:
CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) );
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | INT(11) | 用户唯一标识,自增主键 |
| username | VARCHAR(50) | 用户名,设置为唯一 |
| password | VARCHAR(255) | 哈希后的密码,长度需足够长 |
核心步骤:PHP登录代码实现
我们将把代码分为三个部分:数据库连接文件、登录表单和登录处理逻辑。

数据库连接 (db_connect.php)
将数据库连接信息独立成一个文件,便于维护和复用,这里我们推荐使用PDO(PHP Data Objects),因为它支持多种数据库且预处理语句功能强大,能有效防止SQL注入。
<?php
// db_connect.php
$host = 'localhost';
$dbname = 'your_database_name';
$username = 'your_db_username';
$password = 'your_db_password';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $username, $password, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
创建登录表单 (login.html)
这是一个简单的HTML表单,用于收集用户输入的用户名和密码。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">用户登录</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login.php" method="post">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
</div>
<div>
<button type="submit">登录</button>
</div>
</form>
</body>
</html>
处理登录逻辑 (login.php)
这是整个登录系统的核心,它负责接收表单数据、查询数据库、验证密码并启动会话。
<?php
// 开启Session
session_start();
// 引入数据库连接文件
require_once 'db_connect.php';
// 检查请求是否为POST
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 获取并清理输入
$username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_STRING);
$password = $_POST['password']; // 密码不需要清理,用于验证
if (empty($username) || empty($password)) {
die("用户名和密码不能为空!");
}
try {
// 准备SQL查询语句,防止SQL注入
$stmt = $pdo->prepare("SELECT id, username, password FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
// 验证用户是否存在以及密码是否正确
if ($user && password_verify($password, $user['password'])) {
// 密码正确,登录成功,设置Session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
// 重定向到用户主页或其他受保护页面
header("Location: dashboard.php");
exit();
} else {
// 用户名或密码错误
echo "登录失败:用户名或密码不正确。";
}
} catch (\PDOException $e) {
die("数据库查询失败: " . $e->getMessage());
}
} else {
// 如果不是POST请求,重定向到登录页面
header("Location: login.html");
exit();
}
?>
安全要点小编总结
- 使用PDO或MySQLi:它们都支持预处理语句,是防止SQL注入的标准做法。
- 密码哈希:务必使用
password_hash()存储密码,用password_verify()进行验证。 - 输入验证:使用
filter_input()等函数对外部输入进行过滤和清理。 - HTTPS:在生产环境中,务必使用HTTPS协议加密传输数据,防止密码在传输过程中被窃取。
- 会话管理:安全地配置
session,例如设置session.cookie_httponly和session.cookie_secure。
相关问答FAQs
为什么不能直接将用户密码明文存储在数据库中?
答:将密码以明文形式存储是极其危险的行为,一旦数据库泄露(这在现实中时有发生),所有用户的密码将直接暴露给攻击者,攻击者可以轻易地用这些密码去尝试登录用户的其他账户(因为很多人在不同网站使用相同密码),造成连锁性的安全事件,通过哈希处理(如password_hash()),即使数据库泄露,攻击者得到的也只是无法逆向解密的哈希字符串,从而保护了用户的原始密码。

MySQLi 和 PDO 有什么区别,我应该选择哪个? 答:两者都是PHP中优秀的数据库扩展,主要区别在于:
- 数据库支持:MySQLi仅支持MySQL数据库,而PDO支持多种数据库系统(如MySQL, PostgreSQL, SQLite等),具有更好的可移植性。
- API风格:PDO提供统一的面向对象API,而MySQLi同时提供面向对象和面向过程两种风格。
- 命名参数:PDO支持命名参数(如
username),在复杂查询中代码更清晰易读,MySQLi只支持问号占位符。 选择建议:对于新项目,强烈推荐使用PDO,它的灵活性、安全性和更现代的API使其成为当前PHP开发的主流选择,如果你确定项目将永远只使用MySQL,并且偏爱MySQLi的某些特定功能,那么MySQLi也是一个可行的选项。