本文目录导读:

我来帮你实现一个完整的PHP手机号验证码登录系统,这个系统包含后端API和前端演示页面。
数据库设计
-- 用户表 CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `phone` varchar(20) NOT NULL COMMENT '手机号', `password` varchar(255) DEFAULT NULL COMMENT '密码(可选)', `nickname` varchar(50) DEFAULT NULL, `avatar` varchar(255) DEFAULT NULL, `status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 验证码表 CREATE TABLE `sms_codes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `phone` varchar(20) NOT NULL COMMENT '手机号', `code` varchar(6) NOT NULL COMMENT '验证码', `expire_time` datetime NOT NULL COMMENT '过期时间', `ip` varchar(50) DEFAULT NULL COMMENT 'IP地址', `used` tinyint(1) DEFAULT '0' COMMENT '是否已使用', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_phone` (`phone`), KEY `idx_created` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
后端PHP代码
config.php - 配置文件
<?php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_USER', 'root');
define('DB_PASS', '');
// 短信配置(示例使用阿里云短信)
define('ALIYUN_SMS_ACCESS_KEY_ID', 'your_access_key_id');
define('ALIYUN_SMS_ACCESS_KEY_SECRET', 'your_access_key_secret');
define('ALIYUN_SMS_SIGN_NAME', '你的签名');
define('ALIYUN_SMS_TEMPLATE_CODE', 'SMS_123456789'); // 验证码模板
// Session配置
session_start();
// 数据库连接
function db_connect() {
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
DB_USER,
DB_PASS
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch (PDOException $e) {
die('数据库连接失败: ' . $e->getMessage());
}
}
?>
send_sms.php - 发送验证码
<?php
require_once 'config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['code' => 405, 'msg' => '请求方法不允许']);
exit;
}
$phone = $_POST['phone'] ?? '';
// 验证手机号格式
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
echo json_encode(['code' => 400, 'msg' => '手机号格式不正确']);
exit;
}
// 检查发送频率(同一手机号60秒内只能发一次)
$pdo = db_connect();
$stmt = $pdo->prepare("SELECT * FROM sms_codes WHERE phone = ? AND created_at > DATE_SUB(NOW(), INTERVAL 60 SECOND) ORDER BY id DESC LIMIT 1");
$stmt->execute([$phone]);
if ($stmt->fetch()) {
echo json_encode(['code' => 429, 'msg' => '发送太频繁,请稍后再试']);
exit;
}
// 检查IP发送频率(同一IP 24小时内最多发10次)
$ip = $_SERVER['REMOTE_ADDR'];
$stmt = $pdo->prepare("SELECT COUNT(*) FROM sms_codes WHERE ip = ? AND created_at > DATE_SUB(NOW(), INTERVAL 24 HOUR)");
$stmt->execute([$ip]);
if ($stmt->fetchColumn() >= 10) {
echo json_encode(['code' => 429, 'msg' => '今日发送次数已达上限']);
exit;
}
// 生成6位随机验证码
$code = str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT);
// 保存验证码到数据库(有效时间5分钟)
$expire_time = date('Y-m-d H:i:s', time() + 300);
$stmt = $pdo->prepare("INSERT INTO sms_codes (phone, code, expire_time, ip) VALUES (?, ?, ?, ?)");
$stmt->execute([$phone, $code, $expire_time, $ip]);
// 发送短信(使用阿里云短信服务)
$result = sendSms($phone, $code);
if ($result['status']) {
echo json_encode(['code' => 200, 'msg' => '验证码发送成功']);
} else {
echo json_encode(['code' => 500, 'msg' => '验证码发送失败']);
}
/**
* 发送短信函数(阿里云短信)
*/
function sendSms($phone, $code) {
// 这里实现阿里云短信发送
// 首先需要引入阿里云SDK
// 为了演示,这里返回成功(实际开发时替换为真实短信服务)
try {
// 实际代码示例:
/*
$accessKeyId = ALIYUN_SMS_ACCESS_KEY_ID;
$accessKeySecret = ALIYUN_SMS_ACCESS_KEY_SECRET;
// 引入阿里云短信SDK
// require_once 'aliyun-php-sdk/aliyun-php-sdk-core/Config.php';
// require_once 'aliyun-php-sdk/aliyun-php-sdk-core/Profile/DefaultProfile.php';
// require_once 'aliyun-php-sdk/aliyun-php-sdk-core/DefaultAcsClient.php';
// $profile = DefaultProfile::getProfile("cn-hangzhou", $accessKeyId, $accessKeySecret);
// $client = new DefaultAcsClient($profile);
// $request = new Dysmsapi\Request\V20170525\SendSmsRequest();
// $request->setPhoneNumbers($phone);
// $request->setSignName(ALIYUN_SMS_SIGN_NAME);
// $request->setTemplateCode(ALIYUN_SMS_TEMPLATE_CODE);
// $request->setTemplateParam(json_encode(['code' => $code]));
// $response = $client->getAcsResponse($request);
// return ['status' => true, 'message' => '发送成功'];
*/
// 演示环境直接返回成功
return ['status' => true, 'message' => '模拟发送成功'];
} catch (Exception $e) {
return ['status' => false, 'message' => $e->getMessage()];
}
}
?>
verify_code.php - 验证验证码
<?php
require_once 'config.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['code' => 405, 'msg' => '请求方法不允许']);
exit;
}
$phone = $_POST['phone'] ?? '';
$code = $_POST['code'] ?? '';
// 验证输入
if (empty($phone) || empty($code)) {
echo json_encode(['code' => 400, 'msg' => '请填写手机号和验证码']);
exit;
}
$pdo = db_connect();
// 查询最新的验证码
$stmt = $pdo->prepare("
SELECT * FROM sms_codes
WHERE phone = ? AND used = 0
ORDER BY id DESC LIMIT 1
");
$stmt->execute([$phone]);
$sms_data = $stmt->fetch(PDO::FETCH_ASSOC);
// 验证验证码
if (!$sms_data) {
echo json_encode(['code' => 400, 'msg' => '验证码不存在,请重新获取']);
exit;
}
if (strtotime($sms_data['expire_time']) < time()) {
echo json_encode(['code' => 400, 'msg' => '验证码已过期,请重新获取']);
exit;
}
if ($sms_data['code'] !== $code) {
echo json_encode(['code' => 400, 'msg' => '验证码错误']);
exit;
}
// 标记验证码已使用
$stmt = $pdo->prepare("UPDATE sms_codes SET used = 1 WHERE id = ?");
$stmt->execute([$sms_data['id']]);
// 检查用户是否存在,不存在则自动注册
$stmt = $pdo->prepare("SELECT * FROM users WHERE phone = ?");
$stmt->execute([$phone]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
// 自动注册新用户
$stmt = $pdo->prepare("INSERT INTO users (phone, nickname) VALUES (?, ?)");
$stmt->execute([$phone, '用户' . substr($phone, -4)]);
$user_id = $pdo->lastInsertId();
} else {
$user_id = $user['id'];
// 检查用户状态
if ($user['status'] == 0) {
echo json_encode(['code' => 403, 'msg' => '账号已被禁用']);
exit;
}
}
// 更新用户登录信息(这里需要token机制)
$token = bin2hex(random_bytes(32));
$_SESSION['user_id'] = $user_id;
$_SESSION['phone'] = $phone;
$_SESSION['token'] = $token;
echo json_encode([
'code' => 200,
'msg' => '登录成功',
'data' => [
'user_id' => $user_id,
'phone' => $phone,
'token' => $token,
'nickname' => $user['nickname'] ?? '新用户'
]
]);
?>
login_api.php - 登录接口(整合版)
<?php
require_once 'config.php';
header('Content-Type: application/json');
$action = $_GET['action'] ?? '';
$method = $_SERVER['REQUEST_METHOD'];
switch ($action) {
case 'send_code':
if ($method !== 'POST') {
echo json_encode(['code' => 405, 'msg' => 'Method Not Allowed']);
break;
}
$phone = $_POST['phone'] ?? '';
// 验证手机号
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
echo json_encode(['code' => 400, 'msg' => '手机号格式不正确']);
break;
}
// 生成验证码
$code = str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT);
// 检查是否频繁发送
$pdo = db_connect();
$stmt = $pdo->prepare("SELECT COUNT(*) FROM sms_codes WHERE phone = ? AND created_at > DATE_SUB(NOW(), INTERVAL 60 SECOND)");
$stmt->execute([$phone]);
if ($stmt->fetchColumn() > 0) {
echo json_encode(['code' => 429, 'msg' => '发送太频繁,请1分钟后再试']);
break;
}
// 保存验证码
$expire_at = date('Y-m-d H:i:s', time() + 300); // 5分钟有效
$stmt = $pdo->prepare("INSERT INTO sms_codes (phone, code, expire_time) VALUES (?, ?, ?)");
$stmt->execute([$phone, $code, $expire_at]);
// 调用短信服务(此处替换为真实短信服务)
$sms_sent = sendSmsCode($phone, $code);
if ($sms_sent) {
echo json_encode(['code' => 200, 'msg' => '验证码发送成功', 'debug_code' => $code]);
} else {
echo json_encode(['code' => 500, 'msg' => '短信发送失败']);
}
break;
case 'login':
if ($method !== 'POST') {
echo json_encode(['code' => 405, 'msg' => 'Method Not Allowed']);
break;
}
$phone = $_POST['phone'] ?? '';
$code = $_POST['code'] ?? '';
if (empty($phone) || empty($code)) {
echo json_encode(['code' => 400, 'msg' => '手机号和验证码不能为空']);
break;
}
// 验证验证码(5分钟内有效,且只能使用一次)
$pdo = db_connect();
$stmt = $pdo->prepare("
SELECT * FROM sms_codes
WHERE phone = ? AND used = 0
AND expire_time > NOW()
ORDER BY id DESC LIMIT 1
");
$stmt->execute([$phone]);
$sms = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$sms || $sms['code'] !== $code) {
echo json_encode(['code' => 400, 'msg' => '验证码无效或已过期']);
break;
}
// 标记验证码已使用
$stmt = $pdo->prepare("UPDATE sms_codes SET used = 1 WHERE id = ?");
$stmt->execute([$sms['id']]);
// 查找或创建用户
$stmt = $pdo->prepare("SELECT * FROM users WHERE phone = ?");
$stmt->execute([$phone]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
// 注册新用户
$stmt = $pdo->prepare("INSERT INTO users (phone, nickname) VALUES (?, ?)");
$nickname = '用户' . substr($phone, -4);
$stmt->execute([$phone, $nickname]);
$userId = $pdo->lastInsertId();
$user = [
'id' => $userId,
'phone' => $phone,
'nickname' => $nickname
];
}
// 生成登录token
$token = bin2hex(random_bytes(32));
$_SESSION['user_id'] = $user['id'];
$_SESSION['token'] = $token;
echo json_encode([
'code' => 200,
'msg' => '登录成功',
'data' => [
'user_id' => $user['id'],
'phone' => $user['phone'],
'nickname' => $user['nickname'],
'token' => $token
]
]);
break;
case 'check_login':
// 检查登录状态
session_start();
if (isset($_SESSION['user_id'])) {
echo json_encode([
'code' => 200,
'data' => [
'user_id' => $_SESSION['user_id'],
'phone' => $_SESSION['phone'] ?? ''
]
]);
} else {
echo json_encode(['code' => 401, 'msg' => '未登录']);
}
break;
case 'logout':
session_start();
session_destroy();
echo json_encode(['code' => 200, 'msg' => '退出成功']);
break;
default:
echo json_encode(['code' => 404, 'msg' => '接口不存在']);
}
前端演示页面
login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">手机号验证码登录</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.login-container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
padding: 40px;
width: 100%;
max-width: 400px;
}
h2 {
text-align: center;
margin-bottom: 30px;
color: #333;
font-size: 24px;
font-weight: 500;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #555;
font-size: 14px;
font-weight: 500;
}
input[type="tel"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
transition: border-color 0.3s;
}
input[type="tel"]:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.input-group {
display: flex;
gap: 10px;
}
.input-group input {
flex: 1;
}
.btn-send-code {
background: #f0f0f0;
border: 1px solid #ddd;
border-radius: 6px;
color: #333;
cursor: pointer;
font-size: 14px;
padding: 12px 16px;
white-space: nowrap;
transition: all 0.3s;
min-width: 130px;
}
.btn-send-code:hover:not(:disabled) {
background: #e0e0e0;
}
.btn-send-code:disabled {
color: #999;
cursor: not-allowed;
background: #f5f5f5;
}
.btn-login {
width: 100%;
padding: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 6px;
color: white;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: opacity 0.3s;
margin-top: 10px;
}
.btn-login:hover:not(:disabled) {
opacity: 0.9;
}
.btn-login:disabled {
background: #cccccc;
cursor: not-allowed;
}
#message {
text-align: center;
margin-top: 15px;
font-size: 14px;
min-height: 20px;
}
.success {
color: #4CAF50;
}
.error {
color: #f44336;
}
.error-message {
color: #f44336;
font-size: 12px;
margin-top: 5px;
min-height: 16px;
}
.login-info {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 6px;
text-align: center;
color: #555;
}
</style>
</head>
<body>
<div class="login-container">
<h2>手机号登录</h2>
<div class="form-group">
<label for="phone">手机号</label>
<input type="tel" id="phone" placeholder="请输入手机号" maxlength="11">
<div class="error-message" id="phoneError"></div>
</div>
<div class="form-group">
<label for="code">验证码</label>
<div class="input-group">
<input type="text" id="code" placeholder="请输入验证码" maxlength="6">
<button class="btn-send-code" id="sendCodeBtn" onclick="sendCode()">获取验证码</button>
</div>
<div class="error-message" id="codeError"></div>
</div>
<button class="btn-login" id="loginBtn" onclick="login()">登录</button>
<div id="message"></div>
<div class="login-info" id="loginInfo" style="display: none;">
<p>✅ 登录成功!</p>
<p id="userInfo"></p>
<button class="btn-login" style="background: #f44336; margin-top: 10px;" onclick="logout()">退出登录</button>
</div>
</div>
<script>
let countdown = 0;
let timer = null;
// 发送验证码
async function sendCode() {
const phone = document.getElementById('phone').value.trim();
const phoneError = document.getElementById('phoneError');
const sendBtn = document.getElementById('sendCodeBtn');
// 清空错误信息
phoneError.textContent = '';
// 验证手机号
if (!phone) {
phoneError.textContent = '请输入手机号';
return;
}
if (!/^1[3-9]\d{9}$/.test(phone)) {
phoneError.textContent = '手机号格式不正确';
return;
}
// 禁用按钮
sendBtn.disabled = true;
sendBtn.textContent = '发送中...';
try {
const response = await fetch('login_api.php?action=send_code', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `phone=${phone}`
});
const data = await response.json();
if (data.code === 200) {
showMessage('验证码发送成功!', 'success');
// 开始倒计时
startCountdown(sendBtn);
// 开发调试用,实际生产环境不要输出
if (data.debug_code) {
console.log('开发环境验证码:', data.debug_code);
// 自动填入验证码,方便调试
document.getElementById('code').value = data.debug_code;
}
} else {
showMessage(data.msg || '发送失败', 'error');
sendBtn.disabled = false;
sendBtn.textContent = '获取验证码';
}
} catch (error) {
showMessage('网络错误,请稍后再试', 'error');
sendBtn.disabled = false;
sendBtn.textContent = '获取验证码';
}
}
// 登录
async function login() {
const phone = document.getElementById('phone').value.trim();
const code = document.getElementById('code').value.trim();
const phoneError = document.getElementById('phoneError');
const codeError = document.getElementById('codeError');
const loginBtn = document.getElementById('loginBtn');
// 清空错误信息
phoneError.textContent = '';
codeError.textContent = '';
// 验证表单
if (!phone) {
phoneError.textContent = '请输入手机号';
return;
}
if (!/^1[3-9]\d{9}$/.test(phone)) {
phoneError.textContent = '手机号格式不正确';
return;
}
if (!code || code.length !== 6) {
codeError.textContent = '请输入6位验证码';
return;
}
// 禁用登录按钮
loginBtn.disabled = true;
loginBtn.textContent = '登录中...';
try {
const response = await fetch('login_api.php?action=login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `phone=${phone}&code=${code}`
});
const result = await response.json();
if (result.code === 200) {
showMessage('登录成功!', 'success');
// 显示登录成功信息
document.getElementById('loginInfo').style.display = 'block';
document.getElementById('userInfo').textContent =
`用户ID: ${result.data.user_id} | 手机号: ${result.data.phone} | 昵称: ${result.data.nickname}`;
// 保存token到localStorage
localStorage.setItem('token', result.data.token);
// 隐藏登录表单
setTimeout(() => {
document.querySelector('.form-group').style.display = 'none';
document.getElementById('sendCodeBtn').style.display = 'none';
document.getElementById('loginBtn').style.display = 'none';
}, 500);
} else {
showMessage(result.msg || '登录失败', 'error');
}
} catch (error) {
showMessage('网络错误,请稍后再试', 'error');
} finally {
loginBtn.disabled = false;
loginBtn.textContent = '登录';
}
}
// 退出登录
function logout() {
fetch('login_api.php?action=logout')
.then(response => response.json())
.then(data => {
if (data.code === 200) {
localStorage.removeItem('token');
location.reload();
}
});
}
// 倒计时
function startCountdown(btn) {
countdown = 60;
timer = setInterval(() => {
countdown--;
if (countdown <= 0) {
clearInterval(timer);
btn.disabled = false;
btn.textContent = '获取验证码';
} else {
btn.textContent = countdown + 's后重发';
}
}, 1000);
}
// 显示消息
function showMessage(msg, type) {
const messageEl = document.getElementById('message');
messageEl.textContent = msg;
messageEl.className = type;
// 3秒后清除消息
setTimeout(() => {
messageEl.textContent = '';
messageEl.className = '';
}, 3000);
}
// 实时验证手机号格式
document.getElementById('phone').addEventListener('input', function(e) {
const phone = e.target.value.replace(/\D/g, '');
e.target.value = phone;
if (phone.length === 11 && !/^1[3-9]\d{9}$/.test(phone)) {
document.getElementById('phoneError').textContent = '手机号格式不正确';
} else {
document.getElementById('phoneError').textContent = '';
}
});
// 验证码只能输入数字
document.getElementById('code').addEventListener('input', function(e) {
e.target.value = e.target.value.replace(/\D/g, '');
});
// 检查登录状态
window.onload = function() {
const token = localStorage.getItem('token');
if (token) {
fetch('login_api.php?action=check_login')
.then(response => response.json())
.then(data => {
if (data.code === 200 && data.data) {
// 自动登录
document.getElementById('loginInfo').style.display = 'block';
document.getElementById('userInfo').textContent =
`用户ID: ${data.data.user_id} | 手机号: ${data.data.phone}`;
document.querySelector('.form-group').style.display = 'none';
document.getElementById('sendCodeBtn').style.display = 'none';
document.getElementById('loginBtn').style.display = 'none';
}
});
}
};
</script>
</body>
</html>
优化建议
安全增强 (security.php)
<?php
// 添加安全相关的类和函数
class Security {
/**
* 防止SQL注入
*/
public static function cleanInput($data) {
return htmlspecialchars(strip_tags(trim($data)));
}
/**
* 生成JWT Token
*/
public static function generateJWT($user_id, $secret_key) {
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
$payload = json_encode([
'user_id' => $user_id,
'iat' => time(),
'exp' => time() + 3600 // 1小时有效
]);
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret_key, true);
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
}
/**
* 验证JWT Token
*/
public static function verifyJWT($token, $secret_key) {
$tokenParts = explode('.', $token);
if (count($tokenParts) !== 3) return false;
$header = base64_decode($tokenParts[0]);
$payload = base64_decode($tokenParts[1]);
$signature = $tokenParts[2];
$expectedSignature = hash_hmac('sha256', $tokenParts[0] . "." . $tokenParts[1], $secret_key, true);
$expectedBase64 = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($expectedSignature));
if (!hash_equals($expectedBase64, $signature)) {
return false;
}
$payloadData = json_decode($payload, true);
if ($payloadData['exp'] < time()) {
return false; // Token过期
}
return $payloadData;
}
}
?>
使用说明
- 配置数据库:根据实际情况修改config.php中的数据库连接信息
- 配置短信服务:如果你有阿里云/腾讯云短信服务,在send_sms.php中配置
- 部署到服务器:将文件部署到支持PHP的服务器
- 测试流程:
- 打开login.html
- 输入手机号,点击获取验证码
- 查看数据库中sms_codes表的验证码(或查看控制台输出的调试验证码)
- 输入验证码登录
注意事项
- 生产环境:一定要禁用debug_code输出
- 邮件验证:可以考虑添加邮件验证码功能
- 验证码存储:生产环境建议使用Redis等缓存数据库
- Rate Limit:生产环境建议增加更完善的限流机制
- HTTPS:生产环境必须使用HTTPS加密传输
- 日志记录:建议记录所有登录操作日志
这个系统已经够用于学习或中小型项目,如果是在生产环境使用,建议进一步增强安全性。