完整的PHP登录注册系统源码:包含PHP+HTML+jQuery+CSS+MySQL,数据采用AJAX异步提交
近在带几个新人做项目,发现很多初学者对“登录注册”这个最基础的功能,依然停留在“照着教程敲一遍,能跑就行”的阶段。但真要把这个模块做到安全、健壮、用户体验好,里面的门道其实不少。
正好之前写了一套基于PHP+MySQL+jQuery的登录注册系统(附完整源码),今天借这个机会,把整个设计和实现思路拆开揉碎了讲讲。希望看完这篇文章,你不仅能自己写出来,还能明白为什么这么写。
一、为什么还需要自己写登录注册?
很多框架(Laravel、Symfony)都内置了用户认证,甚至还有像JWT这样的现成方案。但为什么我依然建议初学者手写一遍?因为认证是一个系统的基石,你只有亲手处理过密码哈希、会话管理、SQL注入防护,才能真正理解框架替我们做了什么。
另外,在实际工作中,很多老旧项目或轻量级站点并不适合引入重型框架,手写一套轻量的认证模块往往更灵活。
二、技术栈选型:为什么是这些?
-
PHP 8.x:服务端语言,简单直接,部署方便。
-
MySQL:存储用户数据,关系型数据库成熟稳定。
-
jQuery + AJAX:用AJAX做异步提交,页面无刷新,体验流畅。jQuery虽然不再是前端主流,但在快速开发中依然很顺手。
-
HTML/CSS:基础结构,配合flex/grid做了简单的响应式设计。
这套组合“老套”但实用,尤其适合初学者理解前后端交互的全过程。
三、系统设计:前后端混合但分离交互
我们采用传统多页面架构 + 局部异步刷新:页面由后端返回(index.html),但表单提交走AJAX。这样既保持了简单性,又避免了表单提交后整页刷新的闪烁感。
四、部分页面展示:
符合现代审美的页面样式:

密码可显示和隐藏:

注册页面:

好了,下面来看看项目文件结构:
project/
├── index.html # 登录注册页面
├── style.css # 页面样式
├── config.php # 数据库配置
├── register.php # 注册处理
├── login.php # 登录处理
└── dashboard.php # 登录后的欢迎页(示例)
五、下面直接上代码:
1、数据库表结构 (MySQL)
CREATE DATABASE IF NOT EXISTS auth_demo;
USE auth_demo;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
2、index.html (包含登录/注册表单和jQuery代码)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录注册系统</title>
<link rel="stylesheet" href="style.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<div class="container">
<div class="form-container">
<div class="tabs">
<button class="tab-btn active" id="login-tab">登录</button>
<button class="tab-btn" id="register-tab">注册</button>
</div>
<!-- 登录表单 -->
<form id="login-form" class="form active">
<h2>登录</h2>
<div class="form-group">
<label for="login-username">用户名</label>
<input type="text" id="login-username" name="username" required>
</div>
<div class="form-group">
<label for="login-password">密码</label>
<input type="password" id="login-password" name="password" required>
</div>
<button type="submit" class="submit-btn">登录</button>
<div class="msg" id="login-msg"></div>
</form>
<!-- 注册表单 -->
<form id="register-form" class="form">
<h2>注册</h2>
<div class="form-group">
<label for="reg-username">用户名</label>
<input type="text" id="reg-username" name="username" required>
</div>
<div class="form-group">
<label for="reg-email">邮箱</label>
<input type="email" id="reg-email" name="email" required>
</div>
<div class="form-group">
<label for="reg-password">密码</label>
<input type="password" id="reg-password" name="password" required>
</div>
<div class="form-group">
<label for="reg-confirm">确认密码</label>
<input type="password" id="reg-confirm" name="confirm" required>
</div>
<button type="submit" class="submit-btn">注册</button>
<div class="msg" id="register-msg"></div>
</form>
</div>
</div>
<script>
$(document).ready(function() {
// 切换标签
$('#login-tab').click(function() {
$('#login-tab').addClass('active');
$('#register-tab').removeClass('active');
$('#login-form').addClass('active');
$('#register-form').removeClass('active');
clearMessages();
});
$('#register-tab').click(function() {
$('#register-tab').addClass('active');
$('#login-tab').removeClass('active');
$('#register-form').addClass('active');
$('#login-form').removeClass('active');
clearMessages();
});
function clearMessages() {
$('#login-msg').text('');
$('#register-msg').text('');
}
// 注册表单提交
$('#register-form').submit(function(e) {
e.preventDefault();
let password = $('#reg-password').val();
let confirm = $('#reg-confirm').val();
if (password !== confirm) {
$('#register-msg').text('两次密码不一致').css('color', 'red');
return;
}
$.ajax({
url: 'register.php',
type: 'POST',
data: {
username: $('#reg-username').val(),
email: $('#reg-email').val(),
password: password
},
dataType: 'json',
success: function(response) {
if (response.success) {
$('#register-msg').text('注册成功,请登录').css('color', 'green');
$('#register-form')[0].reset();
// 切换到登录标签
$('#login-tab').click();
} else {
$('#register-msg').text(response.message).css('color', 'red');
}
},
error: function() {
$('#register-msg').text('服务器错误').css('color', 'red');
}
});
});
// 登录表单提交
$('#login-form').submit(function(e) {
e.preventDefault();
$.ajax({
url: 'login.php',
type: 'POST',
data: {
username: $('#login-username').val(),
password: $('#login-password').val()
},
dataType: 'json',
success: function(response) {
if (response.success) {
window.location.href = 'dashboard.php';
} else {
$('#login-msg').text(response.message).css('color', 'red');
}
},
error: function() {
$('#login-msg').text('服务器错误').css('color', 'red');
}
});
});
});
</script>
</body>
</html>
3、style.css (页面样式)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background: white;
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
width: 400px;
max-width: 90%;
}
.form-container {
padding: 30px;
}
.tabs {
display: flex;
margin-bottom: 30px;
border-bottom: 1px solid #ddd;
}
.tab-btn {
flex: 1;
background: none;
border: none;
padding: 10px;
font-size: 16px;
cursor: pointer;
color: #666;
transition: all 0.3s;
border-bottom: 2px solid transparent;
}
.tab-btn.active {
color: #667eea;
border-bottom: 2px solid #667eea;
}
.tab-btn:hover {
color: #667eea;
}
.form {
display: none;
}
.form.active {
display: block;
}
.form h2 {
margin-bottom: 20px;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 16px;
transition: border-color 0.3s;
}
.form-group input:focus {
outline: none;
border-color: #667eea;
}
.submit-btn {
width: 100%;
padding: 12px;
background: #667eea;
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s;
}
.submit-btn:hover {
background: #5a67d8;
}
.msg {
margin-top: 15px;
text-align: center;
font-size: 14px;
}
4、config.php (数据库配置)
<?php
// 数据库配置信息
define('DB_HOST', 'localhost');
define('DB_USER', 'root'); // 修改为你的数据库用户名
define('DB_PASS', ''); // 修改为你的数据库密码
define('DB_NAME', 'auth_demo');
// 创建PDO连接
try {
$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4';
$pdo = new PDO($dsn, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
die('数据库连接失败: ' . $e->getMessage());
}
?>
5、register.php (注册处理)
<?php
header('Content-Type: application/json');
require_once 'config.php';
// 只接受POST请求
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => '非法请求']);
exit;
}
// 获取并清理输入
$username = trim($_POST['username'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
// 简单验证
if (empty($username) || empty($email) || empty($password)) {
echo json_encode(['success' => false, 'message' => '所有字段均为必填']);
exit;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['success' => false, 'message' => '邮箱格式不正确']);
exit;
}
if (strlen($password) < 6) {
echo json_encode(['success' => false, 'message' => '密码至少6位']);
exit;
}
try {
// 检查用户名或邮箱是否已存在
$stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $email]);
if ($stmt->fetch()) {
echo json_encode(['success' => false, 'message' => '用户名或邮箱已存在']);
exit;
}
// 密码哈希
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 插入用户
$stmt = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
$stmt->execute([$username, $email, $hashedPassword]);
echo json_encode(['success' => true, 'message' => '注册成功']);
} catch (PDOException $e) {
// 记录错误日志(生产环境应记录到文件)
error_log($e->getMessage());
echo json_encode(['success' => false, 'message' => '服务器错误,请稍后重试']);
}
?>
6、login.php (登录处理)
<?php
session_start(); // 启用会话
header('Content-Type: application/json');
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => '非法请求']);
exit;
}
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
echo json_encode(['success' => false, 'message' => '用户名和密码不能为空']);
exit;
}
try {
// 查询用户
$stmt = $pdo->prepare("SELECT id, username, password FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $username]); // 支持使用邮箱登录
$user = $stmt->fetch();
if (!$user) {
echo json_encode(['success' => false, 'message' => '用户名或密码错误']);
exit;
}
// 验证密码
if (password_verify($password, $user['password'])) {
// 密码正确,设置session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
echo json_encode(['success' => true, 'message' => '登录成功']);
} else {
echo json_encode(['success' => false, 'message' => '用户名或密码错误']);
}
} catch (PDOException $e) {
error_log($e->getMessage());
echo json_encode(['success' => false, 'message' => '服务器错误,请稍后重试']);
}
?>
7、dashboard.php (登录后的欢迎页示例)
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: index.html');
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>欢迎页</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container" style="text-align: center; padding: 40px;">
<h2>欢迎,<?php echo htmlspecialchars($_SESSION['username']); ?>!</h2>
<p>你已成功登录。</p>
<a href="logout.php" class="submit-btn" style="display: inline-block; width: auto; padding: 10px 20px; text-decoration: none;">退出登录</a>
</div>
</body>
</html>
8、logout.php (退出登录)
<?php
session_start();
session_destroy();
header('Location: index.html');
exit;
?>
9、部署方法:
-
创建数据库:在 MySQL 中执行上面的建表语句,创建 auth_demo 库和 users 表。
-
配置数据库连接:修改 config.php 中的数据库用户名和密码,确保与你的环境一致。
-
放置文件:将所有文件放入 Web 服务器目录(例如 Apache 的 htdocs 下的 project 文件夹)。
-
访问页面:将所有文件放入 Web 服务器目录(例如 Apache 的 htdocs 下的 project 文件夹)。
-
测试:注册新用户,然后登录。成功后跳转到 dashboard.php。
10、安全配置要点
-
密码使用 password_hash() 哈希存储,不可逆。
-
所有数据库操作均使用 PDO 预处理语句,防止 SQL 注入。
-
输出到 HTML 的内容使用 htmlspecialchars() 转义,防止 XSS 攻击。
-
登录后使用 Session 维持状态,避免在客户端存储敏感信息。
-
AJAX 请求返回 JSON 格式,便于前端处理。
六、核心功能实现细节
1. 注册模块(register.php)
注册逻辑分五步:
-
接收并清理输入:使用 trim() 去除空格。
-
基本验证:字段非空、邮箱格式、密码长度。
-
唯一性检查:查询用户名/邮箱是否已存在。
-
密码哈希:使用 password_hash(),算法选 PASSWORD_DEFAULT(目前是bcrypt)。
-
插入数据库:用预处理语句防止SQL注入。
为什么要用预处理语句?如果直接拼接SQL字符串,攻击者可以在用户名里输入 ' OR 1=1; -- 等恶意内容,导致数据泄露或被删。预处理将SQL结构与数据分离,彻底杜绝注入。
为什么密码不能明文存储?
一旦数据库泄露,用户密码直接暴露。用户很可能在其他网站也用相同密码,后果严重。哈希后即使泄露也无法还原原始密码。
七、可以改进的方向
这个项目虽然完整,但离生产级还有距离,你可以尝试扩展:
-
验证码:防止机器人暴力破解。
-
邮箱激活:注册后发送激活链接,确保邮箱真实。
-
记住我:实现自动登录(需处理安全风险)。
-
OAuth登录:集成微信、QQ等第三方登录。
-
更安全的Session:设置HttpOnly、Secure、SameSite等Cookie属性。
八、总结
写一个登录注册系统,看似简单,实则涉及Web开发的方方面面:前端交互、后端逻辑、数据库设计、安全防护。希望通过这个项目拆解,你能理解每个环节背后的“为什么”。
代码不是终点,思想才是。当你以后用框架时,能想到框架替我们解决了哪些问题,又可能带来哪些新的安全隐患,这才算是真正入门了。
所有源码已附在文中,如果你在实践中有任何问题,欢迎留言交流。
更多推荐



所有评论(0)