
本文深入探讨了php用户注册与登录系统中常见的错误,包括变量名冲突导致的数据存储异常、不安全的密码处理方式以及不规范的页面重定向问题。通过分析具体代码案例,提供了基于预处理语句、强密码哈希、正确会话管理和优化页面结构的安全实践方案,旨在帮助开发者构建健壮、安全的php认证系统。
在构建PHP用户认证系统时,开发者常常会遇到各种问题,从数据存储异常到安全漏洞。本教程将针对一个典型的PHP注册与登录代码库进行分析,指出其中存在的关键问题,并提供一套更为安全和规范的解决方案。
原始代码中存在以下几个主要问题:
在 signupp.php 文件中,用户提交的表单数据被赋值给了 $name, $email, $username, $password 等变量。随后,db.php 文件被 require_once 引入,而 db.php 中也定义了 $username 和 $password 用于数据库连接。这种变量名冲突导致在 createUser 函数内部,当尝试插入用户数据时,实际上使用了数据库连接的凭据(root, password)而不是用户提交的注册信息,从而导致数据存储错误。
原始 signupp.php 片段:
立即学习“PHP免费学习笔记(深入)”;
<?php
if (isset($_POST["submit"])) {
$name = $_POST["namee"];
$email = $_POST["emaill"];
$username = $_POST["userme"]; // 用户提交的用户名
$password = $_POST["passme"]; // 用户提交的密码
require_once 'db.php'; // 引入db.php,其中包含 $username="root", $password="password"
require_once 'functions.php';
createUser($conn, $name, $email, $username, $password); // 此时 $username 和 $password 已被 db.php 覆盖
}
// ...尽管 createUser 函数中使用了 password_hash() 对密码进行哈希处理,但在 loginUser 函数中,密码验证逻辑存在严重缺陷:
原始 functions.php (loginUser) 片段:
<?php
function loginUser($conn, $userme, $passme) {
$passHashed = ["passme"]; // 错误:应从数据库获取哈希密码
$checkPwd = password_verify($passme, "passme"); // 错误:应与数据库哈希密码比较
if ($checkPwd === false) {
// ...
}
else if ($checkPwd === true) {
session_start();
$_SESSION["userme"] = $passme["userme"]; // 错误:不应这样设置会话
// ...
}
}为了解决上述问题,我们将对代码进行重构和优化。
db.php 文件保持不变,但需注意其变量名不应与处理用户输入时使用的变量名冲突。
<?php
// db.php
$servername = "localhost";
$dbUsername = "root"; // 更改变量名以避免冲突
$dbPassword = "password"; // 更改变量名以避免冲突
$dbname = "phpshop";
// Create connection
$conn = mysqli_connect($servername, $dbUsername, $dbPassword, $dbname);
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
?>修改 signupp.php 以确保用户输入变量名与数据库连接变量名不冲突,并使用 header() 进行重定向。
<?php
// signupp.php
if (isset($_POST["submit"])) {
$nameInput = $_POST["namee"];
$emailInput = $_POST["emaill"];
$usernameInput = $_POST["userme"];
$passwordInput = $_POST["passme"];
require_once 'db.php';
require_once 'functions.php';
// 调用createUser函数,传入用户输入数据
createUser($conn, $nameInput, $emailInput, $usernameInput, $passwordInput);
} else {
// 如果没有通过POST提交,重定向回注册页面
header("Location: signup.php");
exit();
}
?>重构 createUser 和 loginUser 函数,确保密码安全哈希、正确验证以及预处理语句的使用。
<?php
// functions.php
/**
* 创建新用户并将其信息存储到数据库。
*
* @param mysqli $conn 数据库连接对象。
* @param string $name 用户全名。
* @param string $email 用户邮箱。
* @param string $username 用户名。
* @param string $password 原始密码。
*/
function createUser($conn, $name, $email, $username, $password) {
// 检查用户名或邮箱是否已存在(可选但推荐)
// ...
$sql = "INSERT INTO GuestsLogs (namee, emaill, userme, passme) VALUES (?, ?, ?, ?);";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $sql)) {
// SQL语句预处理失败
header("Location: signup.php?error=stmtfailed");
exit();
}
// 对密码进行哈希处理
$hashedPwd = password_hash($password, PASSWORD_DEFAULT);
// 绑定参数并执行语句
mysqli_stmt_bind_param($stmt, "ssss", $name, $email, $username, $hashedPwd);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt);
// 注册成功,重定向到注册成功页面或登录页面
header("Location: signup.php?success=registered");
exit();
}
/**
* 验证用户登录凭据。
*
* @param mysqli $conn 数据库连接对象。
* @param string $username 用户名。
* @param string $password 用户输入的原始密码。
*/
function loginUser($conn, $username, $password) {
// 1. 通过用户名从数据库获取用户信息
$sql = "SELECT * FROM GuestsLogs WHERE userme = ? OR emaill = ?;";
$stmt = mysqli_stmt_init($conn);
if (!mysqli_stmt_prepare($stmt, $sql)) {
header("Location: login.php?error=stmtfailed");
exit();
}
mysqli_stmt_bind_param($stmt, "ss", $username, $username); // 允许使用用户名或邮箱登录
mysqli_stmt_execute($stmt);
$resultData = mysqli_stmt_get_result($stmt);
if ($row = mysqli_fetch_assoc($resultData)) {
// 2. 验证密码
$hashedPwdFromDb = $row["passme"];
$checkPwd = password_verify($password, $hashedPwdFromDb);
if ($checkPwd === false) {
// 密码不匹配
header("Location: login.php?error=wronglogin");
exit();
} else if ($checkPwd === true) {
// 3. 登录成功,启动会话并设置会话变量
session_start();
$_SESSION["userid"] = $row["id"]; // 假设用户表有id字段
$_SESSION["username"] = $row["userme"];
header("Location: index.php?login=success");
exit();
}
} else {
// 用户名或邮箱不存在
header("Location: login.php?error=wronglogin");
exit();
}
mysqli_stmt_close($stmt);
}
?>与 signupp.php 类似,确保变量名不冲突并使用 header() 重定向。
<?php
// loginn.php
if (isset($_POST["submit"])) {
$usernameInput = $_POST["userme"];
$passwordInput = $_POST["passme"];
require_once 'db.php';
require_once 'functions.php';
loginUser($conn, $usernameInput, $passwordInput);
} else {
header("Location: index.php"); // 未通过POST提交,重定向到首页
exit();
}
?>为了避免HTML结构嵌套问题,建议将 index.php 视为应用程序的主入口或一个独立的页面,而 signup.php 和 login.php 则直接包含表单内容,不应再 include_once 'index.php';。如果需要共享导航或页脚,应创建单独的 header.php 和 footer.php 文件。
index.php (示例):
<?php
session_start(); // 确保会话在此页面开始
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>PHP Login System Home</title>
<meta charset="utf-8">
<style>
body { font: 1em sans-serif; }
.nav-links a { margin-right: 15px; }
</style>
</head>
<body>
<div class="nav-links">
<a href="index.php">Home</a>
<?php
if (isset($_SESSION["username"])) {
echo "<a href='profile.php'>Profile</a>";
echo "<a href='logout.php'>Log Out</a>";
echo "<span>Welcome, " . htmlspecialchars($_SESSION["username"]) . "!</span>";
} else {
echo "<a href='signup.php'>Sign Up</a>";
echo "<a href='login.php'>Log In</a>";
}
?>
</div>
<h1>Welcome to the PHP Login System</h1>
<p>This is the home page content.</p>
</body>
</html>signup.php (示例):
<?php
// 如果需要共享头部,可以这样引入
// include_once 'header.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sign Up</title>
<meta charset="utf-8">
<style>
body { font: 1em sans-serif; }
.form-container { max-width: 400px; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
.form-container input[type="text"], .form-container input[type="email"], .form-container input[type="password"] {
width: calc(100% - 22px); padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 3px;
}
.form-container button { width: 100%; padding: 10px; background-color: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer; }
.form-container button:hover { background-color: #0056b3; }
.error-message { color: red; margin-bottom: 10px; }
.success-message { color: green; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="form-container">
<h2>Sign Up</h2>
<?php
if (isset($_GET["error"])) {
if ($_GET["error"] == "emptyinput") {
echo "<p class='error-message'>Please fill in all fields!</p>";
} else if ($_GET["error"] == "invalidusername") {
echo "<p class='error-message'>Choose a proper username!</p>";
} // ... 其他错误处理
else if ($_GET["error"] == "stmtfailed") {
echo "<p class='error-message'>Something went wrong, try again!</p>";
}
} else if (isset($_GET["success"])) {
if ($_GET["success"] == "registered") {
echo "<p class='success-message'>You have signed up successfully! You can now log in.</p>";
}
}
?>
<form action="signupp.php" method="post">
<input type="text" name="namee" placeholder="Full name..">
<input type="email" name="emaill" placeholder="Email..">
<input type="text" name="userme" placeholder="Username..">
<input type="password" name="passme" placeholder="Password..">
<button type="submit" name="submit">Sign Up</button>
</form>
<p>Already have an account? <a href="login.php">Log In</a></p>
</div>
</body>
</html>
<?php
// 如果需要共享页脚,可以这样引入
// include_once 'footer.php';
?>login.php (示例):
<?php
// include_once 'header.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Log In</title>
<meta charset="utf-8">
<style>
body { font: 1em sans-serif; }
.form-container { max-width: 400px; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
.form-container input[type="text"], .form-container input[type="password"] {
width: calc(100% - 22px); padding: 10px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 3px;
}
.form-container button { width: 100%; padding: 10px; background-color: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer; }
.form-container button:hover { background-color: #0056b3; }
.error-message { color: red; margin-bottom: 10px; }
</style>
</head>
<body>
<div class="form-container">
<h2>Sign In</h2>
<?php
if (isset($_GET["error"])) {
if ($_GET["error"] == "emptyinput") {
echo "<p class='error-message'>Please fill in all fields!</p>";
} else if ($_GET["error"] == "wronglogin") {
echo "<p class='error-message'>Incorrect login information!</p>";
} else if ($_GET["error"] == "stmtfailed") {
echo "<p class='error-message'>Something went wrong, try again!</p>";
}
}
?>
<form action="loginn.php" method="post">
<input type="text" name="userme" placeholder="Username or Email...">
<input type="password" name="passme" placeholder="Password...">
<button type="submit" name="submit">Log In</button>
</form>
<p>Don't have an account? <a href="signup.php">Sign Up</a></p>
</div>
</body>
</html>
<?php
// include_once 'footer.php';
?>logout.php 保持不变,它正确地销毁了会话并重定向。
<?php
// logout.php
session_start();
session_unset();
session_destroy();
header("Location: index.php");
exit();
?>以上就是PHP用户认证系统常见问题与安全实践指南的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号