
前言
做会员站、资源站最常用到的就是登录 / 注册 / 会话。今天分享一套原创教学用途的轻量 PHP 账号系统源码:SQLite 存储、password_hash 密码哈希、CSRF 防护、Session 会话,单目录可部署,适合学习鉴权基础或接到自己的项目里。
声明:本文源码为原创教学示例,请勿用于违法违规用途;请遵守当地法律法规与目标服务条款。
功能特性
- 注册:用户名规则校验、邮箱唯一、密码强度
- 登录:用户名或邮箱 + 密码,失败提示统一
- 会话:登录后
session_regenerate_id防会话固定 - 安全:PDO 预处理防注入、输出转义防 XSS、CSRF Token
- SQLite 免安装,首次运行自动建库并写入演示账号
- 深色简洁 UI,手机可用
环境要求
- PHP 8.0+(需 pdo_sqlite)
- 任意可跑 PHP 的主机 / 宝塔 / Nginx+php-fpm
目录结构
light-auth/
├── index.php # 路由与页面
├── lib.php # 认证核心
├── config.php # 配置
└── data/ # SQLite(自动创建)
└── users.db
完整源码
config.php
<?php
// 轻量登录注册系统 · 配置
return [
'site_name' => '轻账号中心',
'db_path' => __DIR__ . '/data/users.db',
'allow_reg' => true,
'session_name' => 'LIGHTAUTH',
'min_pass_len' => 8,
];
lib.php
<?php
/**
* 轻量认证核心:SQLite + Session + CSRF + 密码哈希
*/
function cfg(string $k, $d = null) {
static $c = null;
if ($c === null) $c = require __DIR__ . '/../config.php';
return $c[$k] ?? $d;
}
function db(): PDO {
static $pdo = null;
if ($pdo instanceof PDO) return $pdo;
$path = cfg('db_path');
if (!is_dir(dirname($path))) mkdir(dirname($path), 0755, true);
$isNew = !file_exists($path);
$pdo = new PDO('sqlite:' . $path);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->exec("CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)");
if ($isNew) {
$pdo->prepare("INSERT INTO users (username, email, password_hash) VALUES (?,?,?)")
->execute(['admin', 'admin@example.com', password_hash('admin12345', PASSWORD_DEFAULT)]);
}
return $pdo;
}
function e(?string $s): string {
return htmlspecialchars((string)$s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
function csrf(): string {
if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(16));
return $_SESSION['csrf'];
}
function csrf_ok(): bool {
return hash_equals(csrf(), (string)($_POST['csrf'] ?? ''));
}
function flash(string $k, ?string $v = null) {
if ($v !== null) { $_SESSION['flash'][$k] = $v; return null; }
$x = $_SESSION['flash'][$k] ?? null;
unset($_SESSION['flash'][$k]);
return $x;
}
function login_user(array $user): void {
session_regenerate_id(true);
$_SESSION['uid'] = (int)$user['id'];
$_SESSION['uname'] = $user['username'];
}
function current_user(): ?array {
if (empty($_SESSION['uid'])) return null;
$st = db()->prepare('SELECT id, username, email, created_at FROM users WHERE id = ?');
$st->execute([$_SESSION['uid']]);
return $st->fetch() ?: null;
}
function require_login(): void {
if (!current_user()) {
header('Location: index.php?r=login');
exit;
}
}
function register_user(string $username, string $email, string $password): array {
$username = trim($username);
$email = trim($email);
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
return ['ok' => false, 'msg' => '用户名需为 3-20 位字母数字下划线'];
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return ['ok' => false, 'msg' => '邮箱格式不正确'];
}
if (strlen($password) < (int)cfg('min_pass_len', 8)) {
return ['ok' => false, 'msg' => '密码至少 ' . cfg('min_pass_len') . ' 位'];
}
$st = db()->prepare('SELECT 1 FROM users WHERE username = ? OR email = ?');
$st->execute([$username, $email]);
if ($st->fetch()) {
return ['ok' => false, 'msg' => '用户名或邮箱已存在'];
}
db()->prepare('INSERT INTO users (username, email, password_hash) VALUES (?,?,?)')
->execute([$username, $email, password_hash($password, PASSWORD_DEFAULT)]);
$st = db()->prepare('SELECT * FROM users WHERE username = ?');
$st->execute([$username]);
login_user($st->fetch());
return ['ok' => true, 'msg' => '注册成功'];
}
function login_by_password(string $account, string $password): array {
$st = db()->prepare('SELECT * FROM users WHERE username = ? OR email = ?');
$st->execute([trim($account), trim($account)]);
$u = $st->fetch();
if (!$u || !password_verify($password, $u['password_hash'])) {
return ['ok' => false, 'msg' => '账号或密码错误'];
}
login_user($u);
return ['ok' => true, 'msg' => '登录成功'];
}
index.php
<?php
declare(strict_types=1);
session_name(cfg('session_name'));
session_start();
require __DIR__ . '/lib.php';
$r = $_GET['r'] ?? 'home';
header('Content-Type: text/html; charset=utf-8');
// 处理 POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!csrf_ok()) {
die('CSRF 校验失败');
}
if ($r === 'register' && cfg('allow_reg')) {
$res = register_user($_POST['username'] ?? '', $_POST['email'] ?? '', $_POST['password'] ?? '');
flash('msg', $res['msg']);
header('Location: index.php?r=' . ($res['ok'] ? 'home' : 'register'));
exit;
}
if ($r === 'login') {
$res = login_by_password($_POST['account'] ?? '', $_POST['password'] ?? '');
flash('msg', $res['msg']);
header('Location: index.php?r=' . ($res['ok'] ? 'home' : 'login'));
exit;
}
if ($r === 'logout') {
session_destroy();
header('Location: index.php');
exit;
}
}
$me = current_user();
$title = cfg('site_name');
function layout_top(string $title, ?array $me): void {
echo '<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">';
echo '<meta name="viewport" content="width=device-width,initial-scale=1">';
echo '<title>' . htmlspecialchars($title) . '</title><style>
body{font-family:system-ui,-apple-system,"PingFang SC",sans-serif;margin:0;background:#0f1115;color:#e8eaed}
.nav{height:60px;display:flex;align-items:center;gap:16px;padding:0 20px;border-bottom:1px solid #2a2f3a;background:#1a1d24}
.nav a{color:#b0b7c4;text-decoration:none;padding:8px 12px;border-radius:8px}
.nav a:hover{color:#f04494;background:rgba(240,68,148,.12)}
.nav .brand{font-weight:700;color:#fff}
.wrap{max-width:420px;margin:48px auto;padding:0 16px}
.card{background:#1a1d24;border:1px solid #2a2f3a;border-radius:16px;padding:28px}
h1{margin:0 0 8px;font-size:22px}
label{display:block;font-size:13px;color:#8b93a7;margin:12px 0 6px}
input{width:100%;box-sizing:border-box;padding:11px 14px;border-radius:10px;border:1px solid #2a2f3a;background:#12151c;color:#fff}
input:focus{outline:0;border-color:#f04494}
button{width:100%;margin-top:18px;padding:12px;border:0;border-radius:999px;background:#f04494;color:#fff;font-weight:600;cursor:pointer}
.msg{padding:10px 12px;border-radius:10px;background:rgba(240,68,148,.12);color:#ff8fbc;font-size:14px;margin-bottom:12px}
.ok{background:rgba(61,214,140,.12);color:#7dffa3}
.muted{color:#8b93a7;font-size:13px;text-align:center;margin-top:16px}
.muted a{color:#f04494}
</style></head><body>
<nav class="nav"><span class="brand">' . htmlspecialchars(cfg('site_name')) . '</span>
<a href="index.php">首页</a>';
if ($me) {
echo '<span style="flex:1"></span><a href="index.php?r=profile">我的资料</a>';
echo '<form method="post" action="index.php?r=logout" style="display:inline">';
echo '<input type="hidden" name="csrf" value="' . htmlspecialchars(csrf()) . '">';
echo '<button type="submit" style="width:auto;padding:8px 14px;margin:0">退出</button></form>';
} else {
echo '<span style="flex:1"></span><a href="index.php?r=login">登录</a><a href="index.php?r=register">注册</a>';
}
echo '</nav><div class="wrap">';
}
function layout_bot(): void {
echo '</div></body></html>';
}
layout_top($title, $me);
$msg = flash('msg');
switch ($r) {
case 'login':
if ($me) { header('Location: index.php'); exit; }
if ($msg) echo '<div class="msg">' . e($msg) . '</div>';
echo '<div class="card"><h1>登录</h1>
<form method="post" action="index.php?r=login">
<input type="hidden" name="csrf" value="' . e(csrf()) . '">
<label>用户名 / 邮箱</label><input name="account" required autofocus>
<label>密码</label><input type="password" name="password" required>
<button type="submit">登录</button>
</form>
<div class="muted">没有账号?<a href="index.php?r=register">去注册</a></div></div>';
break;
case 'register':
if ($me) { header('Location: index.php'); exit; }
if (!cfg('allow_reg')) { echo '<div class="msg">注册已关闭</div>'; break; }
if ($msg) echo '<div class="msg">' . e($msg) . '</div>';
echo '<div class="card"><h1>注册</h1>
<form method="post" action="index.php?r=register">
<input type="hidden" name="csrf" value="' . e(csrf()) . '">
<label>用户名</label><input name="username" required placeholder="3-20 位字母数字下划线">
<label>邮箱</label><input type="email" name="email" required>
<label>密码</label><input type="password" name="password" required minlength="8">
<button type="submit">创建账号</button>
</form>
<div class="muted">已有账号?<a href="index.php?r=login">去登录</a></div></div>';
break;
case 'profile':
require_login();
if ($msg) echo '<div class="msg ok">' . e($msg) . '</div>';
echo '<div class="card"><h1>我的资料</h1>
<p>ID:' . (int)$me['id'] . '</p>
<p>用户名:' . e($me['username']) . '</p>
<p>邮箱:' . e($me['email']) . '</p>
<p>注册时间:' . e($me['created_at']) . '</p>
<div class="muted"><a href="index.php">返回首页</a></div></div>';
break;
default:
if ($msg) echo '<div class="msg">' . e($msg) . '</div>';
echo '<div class="card"><h1>' . e(cfg('site_name')) . '</h1>';
if ($me) {
echo '<p>你好,<strong>' . e($me['username']) . '</strong>,已登录。</p>';
echo '<p class="muted" style="text-align:left"><a href="index.php?r=profile">查看资料</a></p>';
} else {
echo '<p>轻量 PHP 登录注册演示:SQLite 存储、密码哈希、CSRF 防护。</p>';
echo '<p><a href="index.php?r=login"><button type="button" style="width:auto;padding:10px 18px;margin:0">登录</button></a> ';
echo '<a href="index.php?r=register"><button type="button" style="width:auto;padding:10px 18px;margin:0;background:#2a2f3a">注册</button></a></p>';
}
echo '</div>';
}
layout_bot();
部署步骤
- 新建站点,上传
index.php、lib.php、config.php,创建data/目录并给 PHP 写权限。 - 修改
config.php站点名;把演示账号改成你自己的或删除注册行。 - 浏览器打开首页,注册或使用演示账号
admin / admin12345登录(请立刻改掉)。 - 生产环境请关闭错误显示、启用 HTTPS,并限制
data/目录 Web 访问。
安全与合规说明
- 密码使用
password_hash/password_verify,不要用 MD5。 - 请修改演示密码;登录接口可自行加验证码或限流防爆破。
- 禁止用于未授权撞库、非法注册机等场景。
- 本源码仅供学习交流,使用后果自负。
小结
几十行核心代码就能实现可用的登录注册。下一步可扩展:找回密码、第三方登录、记住我(Remember Me)、RBAC 权限。需要增强版可在评论区留言。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END






暂无评论内容