实现用户注册功能(核心功能)

完善Web管理后台页面(users.php, emails.php, logout.php)
pull/1/head
zwq 4 months ago
parent 75308793ba
commit fe37b27513

@ -121,27 +121,66 @@ QUIT
### 启动Web服务器
```bash
cd /mnt/d/mailserver/mailserver
php -S localhost:8080 -t public
```
### 访问管理后台
- 访问http://localhost:8080
- 登录账号:
- 管理员:`admin@test.com` / `123456`
- 普通用户:`user1@test.com` / `123456`
### 功能模块
- **仪表盘** - 查看系统统计和最近邮件
- **用户管理** - 管理用户账号(管理员功能)
- **邮件管理** - 查看和管理邮件
- **过滤规则** - 配置邮件和IP过滤规则
- **系统日志** - 查看服务器操作日志
- **系统设置** - 修改服务器参数(仅管理员)
#### 测试步骤
**1. 启动 Web 服务器(如果还没启动):**
```
cd /mnt/d/mailserver/mailserverphp -S localhost:8080 -t public
```
**2. 测试退出登录功能:**
```
访问 http://localhost:8080/index.php
使用 admin@test.com / 123456 登录
点击右上角"退出"链接
预期:跳转到登录页面,会话已清除
```
**3. 测试用户管理页面(管理员功能):**
```
登录管理员账号 admin@test.com
访问 http://localhost:8080/users.php 或点击菜单"用户管理"
测试创建用户:
邮箱testuser2@test.com
密码123456
勾选"管理员"和"激活"
点击"创建用户"
预期:用户创建成功,出现在列表中
测试编辑用户:
点击某个用户的"编辑"按钮
修改密码、管理员状态、激活状态
点击"保存"
预期:用户信息更新成功
测试删除用户:
点击某个用户的"删除"按钮
确认删除
预期:用户从列表中消失(不能删除自己)
```
**4. 测试邮件管理页面:**
```
访问 http://localhost:8080/emails.php 或点击菜单"邮件管理"
管理员:显示所有邮件
普通用户:只显示自己的收件箱
测试查看邮件:
点击邮件主题或"查看"按钮
预期:弹出模态框显示邮件详情
测试标记已读:
点击未读邮件的"标记已读"
预期:邮件状态变为"已读"
测试删除邮件:
点击"删除"按钮
确认删除
预期:邮件从列表中消失
测试分页如果有超过20封邮件
点击页码或"上一页"/"下一页"
预期:正确切换页面
```
**5. 测试权限控制:**
```
使用普通用户 user1@test.com / 123456 登录
尝试访问 http://localhost:8080/users.php
预期:显示"权限不足:只有管理员可以访问此页面"
访问 http://localhost:8080/emails.php
预期:只显示该用户的收件箱邮件
```
## 查看数据
### 方法1phpMyAdmin推荐

@ -5,11 +5,11 @@
*/
return [
'host' => getenv('DB_HOST') ?: '172.17.0.1', // Docker容器网关IP
'port' => getenv('DB_PORT') ?: '3306',
'host' => getenv('DB_HOST') ?: '127.0.0.1', // 使用127.0.0.1连接映射端口
'port' => getenv('DB_PORT') ?: '3308', // Docker映射端口
'database' => getenv('DB_DATABASE') ?: 'mail_server',
'username' => getenv('DB_USERNAME') ?: 'root',
'password' => getenv('DB_PASSWORD') ?: 'MailServerRoot123!',
'username' => getenv('DB_USERNAME') ?: 'mail_user',
'password' => getenv('DB_PASSWORD') ?: 'user123',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'options' => [

@ -0,0 +1,240 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/EmailRepository.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
$emailRepo = new EmailRepository();
$message = '';
$error = '';
// 处理删除邮件
if (isset($_GET['delete'])) {
$emailId = (int)$_GET['delete'];
if ($emailRepo->delete($emailId)) {
$message = "邮件删除成功";
} else {
$error = "删除失败";
}
}
// 处理标记已读
if (isset($_GET['mark_read'])) {
$emailId = (int)$_GET['mark_read'];
if ($emailRepo->markAsRead($emailId)) {
$message = "邮件已标记为已读";
}
}
// 获取邮件列表
$isAdmin = $_SESSION['is_admin'] ?? false;
$userId = $_SESSION['user_id'];
// 分页参数
$page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1;
$perPage = 20;
$offset = ($page - 1) * $perPage;
// 获取邮件
if ($isAdmin) {
$emails = $emailRepo->getAll($perPage, $offset);
$totalEmails = $emailRepo->getCount();
} else {
$emails = $emailRepo->getInbox($userId, $perPage, $offset);
$totalEmails = $emailRepo->getCount($userId);
}
$totalPages = ceil($totalEmails / $perPage);
?>
<!DOCTYPE html>
<html>
<head>
<title>邮件管理 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background: #f8f9fa; font-weight: 600; }
tr:hover { background: #f8f9fa; }
.btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-primary { background: #007bff; color: white; }
.btn-danger { background: #dc3545; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-small { padding: 4px 8px; font-size: 12px; }
.badge { padding: 4px 8px; border-radius: 3px; font-size: 12px; font-weight: 500; }
.badge-read { background: #6c757d; color: white; }
.badge-unread { background: #007bff; color: white; }
.email-unread { font-weight: bold; }
.pagination { margin-top: 20px; text-align: center; }
.pagination a { display: inline-block; padding: 8px 12px; margin: 0 4px; text-decoration: none; border: 1px solid #ddd; border-radius: 4px; }
.pagination a:hover { background: #f8f9fa; }
.pagination .current { background: #007bff; color: white; border-color: #007bff; }
.email-preview { max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); overflow: auto; }
.modal-content { background: white; margin: 50px auto; padding: 20px; width: 80%; max-width: 800px; border-radius: 5px; }
.close { float: right; font-size: 28px; font-weight: bold; cursor: pointer; }
.email-body { white-space: pre-wrap; background: #f8f9fa; padding: 15px; border-radius: 5px; margin-top: 10px; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<?php if ($isAdmin): ?>
<a href="users.php">用户管理</a>
<?php endif; ?>
<a href="emails.php">邮件管理</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<?php if ($isAdmin): ?>
<a href="settings.php">系统设置</a>
<?php endif; ?>
</div>
<div class="container">
<h2>邮件管理 <?php if ($isAdmin): ?>(全部邮件)<?php else: ?>(我的收件箱)<?php endif; ?></h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<p><?php echo $totalEmails; ?> 封邮件</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>发件人</th>
<th>收件人</th>
<th>主题</th>
<th>状态</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($emails)): ?>
<tr>
<td colspan="7" style="text-align: center; padding: 40px;">
暂无邮件
</td>
</tr>
<?php else: ?>
<?php foreach ($emails as $email): ?>
<tr class="<?php echo $email['is_read'] ? '' : 'email-unread'; ?>">
<td><?php echo $email['id']; ?></td>
<td><?php echo htmlspecialchars($email['sender_name'] ?? $email['sender'] ?? '未知'); ?></td>
<td><?php echo htmlspecialchars($email['recipient_name'] ?? $email['recipient'] ?? '未知'); ?></td>
<td class="email-preview">
<a href="#" onclick="viewEmail(<?php echo htmlspecialchars(json_encode($email)); ?>); return false;">
<?php echo htmlspecialchars($email['subject'] ?? '(无主题)'); ?>
</a>
</td>
<td>
<?php if ($email['is_read']): ?>
<span class="badge badge-read">已读</span>
<?php else: ?>
<span class="badge badge-unread">未读</span>
<?php endif; ?>
</td>
<td><?php echo $email['created_at']; ?></td>
<td>
<a href="#" onclick="viewEmail(<?php echo htmlspecialchars(json_encode($email)); ?>); return false;" class="btn btn-primary btn-small">查看</a>
<?php if (!$email['is_read']): ?>
<a href="?mark_read=<?php echo $email['id']; ?>" class="btn btn-success btn-small">标记已读</a>
<?php endif; ?>
<a href="?delete=<?php echo $email['id']; ?>" class="btn btn-danger btn-small" onclick="return confirm('确定要删除此邮件吗?');">删除</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<!-- 分页 -->
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php if ($page > 1): ?>
<a href="?page=<?php echo $page - 1; ?>">上一页</a>
<?php endif; ?>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<?php if ($i == $page): ?>
<span class="current"><?php echo $i; ?></span>
<?php else: ?>
<a href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($page < $totalPages): ?>
<a href="?page=<?php echo $page + 1; ?>">下一页</a>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<!-- 查看邮件模态框 -->
<div id="emailModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeEmailModal()">&times;</span>
<h3 id="email-subject">邮件详情</h3>
<div>
<strong>发件人:</strong><span id="email-sender"></span><br>
<strong>收件人:</strong><span id="email-recipient"></span><br>
<strong>时间:</strong><span id="email-time"></span><br>
<strong>主题:</strong><span id="email-subject-text"></span>
</div>
<div class="email-body" id="email-body"></div>
</div>
</div>
<script>
function viewEmail(email) {
document.getElementById('email-subject').textContent = email.subject || '(无主题)';
document.getElementById('email-subject-text').textContent = email.subject || '(无主题)';
document.getElementById('email-sender').textContent = email.sender_name || email.sender || '未知';
document.getElementById('email-recipient').textContent = email.recipient_name || email.recipient || '未知';
document.getElementById('email-time').textContent = email.created_at;
document.getElementById('email-body').textContent = email.body || '(无内容)';
document.getElementById('emailModal').style.display = 'block';
}
function closeEmailModal() {
document.getElementById('emailModal').style.display = 'none';
}
window.onclick = function(event) {
var modal = document.getElementById('emailModal');
if (event.target == modal) {
closeEmailModal();
}
}
</script>
</body>
</html>

@ -1,36 +1,46 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 简单身份验证
function requireAuth() {
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
header('Location: index.php');
exit;
}
}
// 登录检查
if (isset($_POST['login'])) {
$username = $_POST['username'] ?? '';
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
try {
$db = Database::getInstance();
$stmt = $db->prepare("SELECT id, username, password_hash, is_admin FROM users WHERE username = ? AND is_active = 1");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['is_admin'] = $user['is_admin'];
header('Location: index.php');
exit;
// 检查登录尝试次数(防止暴力破解)
if (!Security::checkLoginAttempts($username)) {
$error = "登录失败次数过多请5分钟后再试";
} else {
$error = "用户名或密码错误";
$userRepo = new UserRepository();
$user = $userRepo->verifyPassword($username, $password);
if ($user && $user['is_active']) {
// 登录成功,清除尝试记录
Security::clearLoginAttempts($username);
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['is_admin'] = $user['is_admin'];
header('Location: index.php');
exit;
} else {
// 登录失败,记录尝试
Security::recordLoginAttempt($username);
$error = "用户名或密码错误";
}
}
} catch (Exception $e) {
$error = "登录失败: " . $e->getMessage();
@ -59,17 +69,20 @@ if (basename($_SERVER['PHP_SELF']) === 'index.php' && !isset($_SESSION['user_id'
<form method="POST">
<div>
<label>用户名:</label>
<input type="text" name="username" value="admin@mail.test" required>
<input type="text" name="username" value="admin@test.com" required>
</div>
<div>
<label>密码:</label>
<input type="password" name="password" value="Admin123!" required>
<input type="password" name="password" value="123456" required>
</div>
<button type="submit" name="login">登录</button>
</form>
<p style="margin-top: 20px; font-size: 12px; color: #666;">
测试账号: admin@mail.test / Admin123!<br>
普通账号: user1@mail.test / User123!
<p style="margin-top: 20px; font-size: 12px; color: #666; text-align: center;">
还没有账号?<a href="register.php" style="color: #007bff; text-decoration: none;">立即注册</a>
</p>
<p style="margin-top: 10px; font-size: 12px; color: #666;">
测试账号: admin@test.com / 123456<br>
普通账号: user1@test.com / 123456
</p>
</div>
</body>
@ -167,10 +180,12 @@ requireAuth();
<tbody>
<?php
$stmt = $db->query("
SELECT e.*, u1.username as sender, u2.username as recipient
SELECT e.*,
COALESCE(u1.username, e.sender) as sender_name,
COALESCE(u2.username, e.recipient) as recipient_name
FROM emails e
JOIN users u1 ON e.sender_id = u1.id
JOIN users u2 ON e.recipient_id = u2.id
LEFT JOIN users u1 ON e.sender_id = u1.id
LEFT JOIN users u2 ON e.recipient_id = u2.id
WHERE e.is_deleted = 0
ORDER BY e.created_at DESC
LIMIT 10
@ -179,9 +194,9 @@ requireAuth();
while ($email = $stmt->fetch()) {
echo "<tr>";
echo "<td>{$email['id']}</td>";
echo "<td>{$email['sender']}</td>";
echo "<td>{$email['recipient']}</td>";
echo "<td>" . htmlspecialchars($email['subject']) . "</td>";
echo "<td>" . htmlspecialchars($email['sender_name'] ?? '未知') . "</td>";
echo "<td>" . htmlspecialchars($email['recipient_name'] ?? '未知') . "</td>";
echo "<td>" . htmlspecialchars($email['subject'] ?? '(无主题)') . "</td>";
echo "<td>{$email['created_at']}</td>";
echo "</tr>";
}

@ -0,0 +1,18 @@
<?php
session_start();
// 清除所有会话数据
$_SESSION = array();
// 销毁会话cookie
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time()-3600, '/');
}
// 销毁会话
session_destroy();
// 重定向到登录页面
header('Location: index.php');
exit;

@ -0,0 +1,288 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/utils/Validator.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
$error = '';
$success = '';
// 处理注册请求
if (isset($_POST['register'])) {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$confirmPassword = $_POST['confirm_password'] ?? '';
// 验证CSRF令牌
if (!Security::verifyCSRFToken($_POST['csrf_token'] ?? '')) {
$error = "安全验证失败,请重试";
} else {
// 验证输入
$usernameValidation = Validator::validateUsername($username);
if (!$usernameValidation['valid']) {
$error = implode('<br>', $usernameValidation['errors']);
} else {
// 验证邮箱域名默认test.com
$domain = 'test.com';
if (!Validator::validateEmailDomain($username, $domain)) {
$error = "邮箱域名必须是 @{$domain}";
} else {
// 验证密码
$passwordValidation = Validator::validatePassword($password, 6);
if (!$passwordValidation['valid']) {
$error = implode('<br>', $passwordValidation['errors']);
} else {
// 验证密码确认
$matchValidation = Validator::validatePasswordMatch($password, $confirmPassword);
if (!$matchValidation['valid']) {
$error = implode('<br>', $matchValidation['errors']);
} else {
// 尝试创建用户
try {
$userRepo = new UserRepository();
// 检查用户名是否已存在
if ($userRepo->usernameExists($username)) {
$error = "该邮箱已被注册";
} else {
// 创建新用户(默认非管理员,激活状态)
$user = $userRepo->create($username, $password, false, true);
if ($user) {
$success = "注册成功!请使用您的账号登录。";
// 3秒后跳转到登录页面
header("Refresh: 3; url=index.php");
} else {
$error = "注册失败,请稍后重试";
}
}
} catch (Exception $e) {
$error = "注册失败: " . $e->getMessage();
}
}
}
}
}
}
}
// 生成CSRF令牌
$csrfToken = Security::generateCSRFToken();
?>
<!DOCTYPE html>
<html>
<head>
<title>用户注册 - 邮件服务器</title>
<meta charset="UTF-8">
<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;
align-items: center;
justify-content: center;
padding: 20px;
}
.register-container {
background: white;
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
padding: 40px;
width: 100%;
max-width: 450px;
}
h1 {
color: #333;
margin-bottom: 10px;
text-align: center;
}
.subtitle {
color: #666;
text-align: center;
margin-bottom: 30px;
font-size: 14px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
transition: border-color 0.3s;
}
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus {
outline: none;
border-color: #667eea;
}
.help-text {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.error {
background: #fee;
color: #c33;
padding: 12px;
border-radius: 5px;
margin-bottom: 20px;
font-size: 14px;
border-left: 4px solid #c33;
}
.success {
background: #efe;
color: #3c3;
padding: 12px;
border-radius: 5px;
margin-bottom: 20px;
font-size: 14px;
border-left: 4px solid #3c3;
}
button[type="submit"] {
width: 100%;
padding: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 5px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
button[type="submit"]:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
button[type="submit"]:active {
transform: translateY(0);
}
.login-link {
text-align: center;
margin-top: 20px;
font-size: 14px;
color: #666;
}
.login-link a {
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.login-link a:hover {
text-decoration: underline;
}
.domain-hint {
display: inline-block;
background: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
font-size: 12px;
}
</style>
</head>
<body>
<div class="register-container">
<h1>用户注册</h1>
<p class="subtitle">创建您的邮件服务器账号</p>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success"><?php echo $success; ?></div>
<?php else: ?>
<form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>">
<div class="form-group">
<label for="username">邮箱地址</label>
<input
type="email"
id="username"
name="username"
value="<?php echo htmlspecialchars($_POST['username'] ?? ''); ?>"
placeholder="example@test.com"
required
autofocus
>
<div class="help-text">请输入您的邮箱地址(域名必须是 <span class="domain-hint">@test.com</span></div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input
type="password"
id="password"
name="password"
placeholder="至少6个字符"
required
minlength="6"
>
<div class="help-text">密码长度至少需要6个字符</div>
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input
type="password"
id="confirm_password"
name="confirm_password"
placeholder="请再次输入密码"
required
minlength="6"
>
</div>
<button type="submit" name="register">注册</button>
</form>
<?php endif; ?>
<div class="login-link">
已有账号?<a href="index.php">立即登录</a>
</div>
</div>
</body>
</html>

@ -0,0 +1,292 @@
<?php
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/utils/Validator.php';
require_once __DIR__ . '/../src/utils/Security.php';
session_start();
// 身份验证
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit;
}
// 检查管理员权限
if (!$_SESSION['is_admin']) {
die('权限不足:只有管理员可以访问此页面');
}
$userRepo = new UserRepository();
$message = '';
$error = '';
// 处理创建用户
if (isset($_POST['create_user'])) {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
$isAdmin = isset($_POST['is_admin']) ? 1 : 0;
$isActive = isset($_POST['is_active']) ? 1 : 0;
$usernameValidation = Validator::validateUsername($username);
if (!$usernameValidation['valid']) {
$error = implode('<br>', $usernameValidation['errors']);
} else {
if (!Validator::validateEmailDomain($username, 'test.com')) {
$error = "邮箱域名必须是 @test.com";
} else {
$passwordValidation = Validator::validatePassword($password, 6);
if (!$passwordValidation['valid']) {
$error = implode('<br>', $passwordValidation['errors']);
} else {
try {
if ($userRepo->usernameExists($username)) {
$error = "用户名已存在";
} else {
$userRepo->create($username, $password, $isAdmin, $isActive);
$message = "用户创建成功";
}
} catch (Exception $e) {
$error = "创建失败: " . $e->getMessage();
}
}
}
}
}
// 处理更新用户
if (isset($_POST['update_user'])) {
$userId = (int)$_POST['user_id'];
$data = [];
if (!empty($_POST['new_password'])) {
$passwordValidation = Validator::validatePassword($_POST['new_password'], 6);
if (!$passwordValidation['valid']) {
$error = implode('<br>', $passwordValidation['errors']);
} else {
$data['password'] = $_POST['new_password'];
}
}
if (isset($_POST['is_admin'])) {
$data['is_admin'] = (int)$_POST['is_admin'];
}
if (isset($_POST['is_active'])) {
$data['is_active'] = (int)$_POST['is_active'];
}
if (empty($error) && !empty($data)) {
if ($userRepo->update($userId, $data)) {
$message = "用户更新成功";
} else {
$error = "更新失败";
}
}
}
// 处理删除用户
if (isset($_GET['delete'])) {
$userId = (int)$_GET['delete'];
if ($userId != $_SESSION['user_id']) { // 不能删除自己
if ($userRepo->delete($userId)) {
$message = "用户删除成功";
} else {
$error = "删除失败";
}
} else {
$error = "不能删除自己的账号";
}
}
// 获取所有用户
$users = $userRepo->getAll();
?>
<!DOCTYPE html>
<html>
<head>
<title>用户管理 - 邮件服务器</title>
<meta charset="UTF-8">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.header { background: #007bff; color: white; padding: 15px; margin: -20px -20px 20px -20px; }
.menu { background: white; padding: 10px; margin-bottom: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.menu a { margin-right: 15px; text-decoration: none; color: #007bff; }
.container { background: white; padding: 20px; border-radius: 5px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.message { background: #d4edda; color: #155724; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
.error { background: #f8d7da; color: #721c24; padding: 12px; border-radius: 5px; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background: #f8f9fa; font-weight: 600; }
tr:hover { background: #f8f9fa; }
.btn { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-primary { background: #007bff; color: white; }
.btn-danger { background: #dc3545; color: white; }
.btn-success { background: #28a745; color: white; }
.btn-small { padding: 4px 8px; font-size: 12px; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; margin-bottom: 5px; font-weight: 500; }
.form-group input, .form-group select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.form-inline { display: flex; gap: 10px; align-items: flex-end; }
.form-inline .form-group { flex: 1; margin-bottom: 0; }
.badge { padding: 4px 8px; border-radius: 3px; font-size: 12px; font-weight: 500; }
.badge-admin { background: #ffc107; color: #000; }
.badge-active { background: #28a745; color: white; }
.badge-inactive { background: #6c757d; color: white; }
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); }
.modal-content { background: white; margin: 50px auto; padding: 20px; width: 500px; border-radius: 5px; }
.close { float: right; font-size: 28px; font-weight: bold; cursor: pointer; }
</style>
</head>
<body>
<div class="header">
<h1>邮件服务器管理后台</h1>
<div>欢迎, <?php echo htmlspecialchars($_SESSION['username']); ?>
(<a href="logout.php" style="color: white;">退出</a>)
</div>
</div>
<div class="menu">
<a href="index.php">仪表盘</a>
<a href="users.php">用户管理</a>
<a href="emails.php">邮件管理</a>
<a href="filters.php">过滤规则</a>
<a href="logs.php">系统日志</a>
<a href="settings.php">系统设置</a>
</div>
<div class="container">
<h2>用户管理</h2>
<?php if ($message): ?>
<div class="message"><?php echo $message; ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<!-- 创建用户表单 -->
<h3>创建新用户</h3>
<form method="POST" class="form-inline">
<div class="form-group">
<label>邮箱地址</label>
<input type="email" name="username" placeholder="user@test.com" required>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" placeholder="至少6个字符" required minlength="6">
</div>
<div class="form-group">
<label>管理员</label>
<input type="checkbox" name="is_admin" value="1">
</div>
<div class="form-group">
<label>激活</label>
<input type="checkbox" name="is_active" value="1" checked>
</div>
<div class="form-group">
<button type="submit" name="create_user" class="btn btn-primary">创建用户</button>
</div>
</form>
<!-- 用户列表 -->
<h3>用户列表 (<?php echo count($users); ?>)</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>角色</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo $user['id']; ?></td>
<td><?php echo htmlspecialchars($user['username']); ?></td>
<td>
<?php if ($user['is_admin']): ?>
<span class="badge badge-admin">管理员</span>
<?php else: ?>
<span>普通用户</span>
<?php endif; ?>
</td>
<td>
<?php if ($user['is_active']): ?>
<span class="badge badge-active">激活</span>
<?php else: ?>
<span class="badge badge-inactive">禁用</span>
<?php endif; ?>
</td>
<td><?php echo $user['created_at']; ?></td>
<td>
<a href="#" onclick="editUser(<?php echo htmlspecialchars(json_encode($user)); ?>); return false;" class="btn btn-primary btn-small">编辑</a>
<?php if ($user['id'] != $_SESSION['user_id']): ?>
<a href="?delete=<?php echo $user['id']; ?>" class="btn btn-danger btn-small" onclick="return confirm('确定要删除此用户吗?');">删除</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- 编辑用户模态框 -->
<div id="editModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeModal()">&times;</span>
<h3>编辑用户</h3>
<form method="POST">
<input type="hidden" name="user_id" id="edit_user_id">
<div class="form-group">
<label>用户名</label>
<input type="text" id="edit_username" readonly style="background: #f5f5f5;">
</div>
<div class="form-group">
<label>新密码(留空则不修改)</label>
<input type="password" name="new_password" placeholder="留空则不修改">
</div>
<div class="form-group">
<label>
<input type="checkbox" name="is_admin" id="edit_is_admin" value="1"> 管理员
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="is_active" id="edit_is_active" value="1"> 激活
</label>
</div>
<button type="submit" name="update_user" class="btn btn-success">保存</button>
<button type="button" onclick="closeModal()" class="btn">取消</button>
</form>
</div>
</div>
<script>
function editUser(user) {
document.getElementById('edit_user_id').value = user.id;
document.getElementById('edit_username').value = user.username;
document.getElementById('edit_is_admin').checked = user.is_admin == 1;
document.getElementById('edit_is_active').checked = user.is_active == 1;
document.getElementById('editModal').style.display = 'block';
}
function closeModal() {
document.getElementById('editModal').style.display = 'none';
}
window.onclick = function(event) {
var modal = document.getElementById('editModal');
if (event.target == modal) {
closeModal();
}
}
</script>
</body>
</html>

@ -2,12 +2,12 @@
<?php
require_once __DIR__ . '/../src/protocol/Pop3Server.php';
echo "🚀 启动最简POP3邮件服务器\n";
echo "启动最简POP3邮件服务器\n";
echo "==============================\n\n";
// 检查是否有权限监听110端口需要sudo
if (posix_getuid() != 0) {
echo "错误需要管理员权限监听110端口\n";
echo "错误需要管理员权限监听110端口\n";
echo "请使用sudo php " . __FILE__ . "\n";
echo "或者使用其他端口(需要修改代码)\n";
exit(1);

@ -2,12 +2,12 @@
<?php
require_once __DIR__ . '/../src/protocol/SmtpServer.php';
echo "🚀 启动最简SMTP邮件服务器\n";
echo "启动最简SMTP邮件服务器\n";
echo "==============================\n\n";
// 检查端口权限
if (posix_getuid() != 0) {
echo "错误需要管理员权限监听25端口\n";
echo "错误需要管理员权限监听25端口\n";
echo "请使用sudo php " . __FILE__ . "\n";
echo "或者使用其他端口(需要修改代码)\n";
exit(1);

@ -0,0 +1,106 @@
<?php
/**
* 测试用户注册功能
* 用法: php scripts/test_register.php
*/
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../src/storage/Database.php';
require_once __DIR__ . '/../src/storage/UserRepository.php';
require_once __DIR__ . '/../src/utils/Validator.php';
require_once __DIR__ . '/../src/utils/Security.php';
echo "=== 用户注册功能测试 ===\n\n";
try {
$userRepo = new UserRepository();
// 测试1: 验证邮箱格式
echo "测试1: 验证邮箱格式\n";
$testEmails = [
'valid@test.com' => true,
'invalid-email' => false,
'test@test.com' => true,
'user@wrong.com' => false,
];
foreach ($testEmails as $email => $expected) {
$isValid = Validator::validateEmail($email);
$domainValid = Validator::validateEmailDomain($email, 'test.com');
$result = $isValid && ($expected ? $domainValid : !$domainValid);
echo " {$email}: " . ($result ? "✓" : "✗") . "\n";
}
// 测试2: 验证密码强度
echo "\n测试2: 验证密码强度\n";
$testPasswords = [
'12345' => false, // 太短
'123456' => true, // 符合最小长度
'password123' => true,
];
foreach ($testPasswords as $password => $expected) {
$validation = Validator::validatePassword($password, 6);
$result = $validation['valid'] === $expected;
echo " '{$password}': " . ($result ? "✓" : "✗") . "\n";
if (!$validation['valid']) {
echo " 错误: " . implode(', ', $validation['errors']) . "\n";
}
}
// 测试3: 检查用户名是否存在
echo "\n测试3: 检查用户名是否存在\n";
$existingUser = $userRepo->findByUsername('admin@test.com');
if ($existingUser) {
echo " admin@test.com 存在: ✓\n";
} else {
echo " admin@test.com 不存在: ✗\n";
}
// 测试4: 创建测试用户(如果不存在)
echo "\n测试4: 创建测试用户\n";
$testUsername = 'testuser@test.com';
if ($userRepo->usernameExists($testUsername)) {
echo " 测试用户已存在,跳过创建\n";
} else {
try {
$newUser = $userRepo->create($testUsername, 'test123456', false, true);
echo " 创建用户成功: ✓\n";
echo " 用户ID: {$newUser['id']}\n";
echo " 用户名: {$newUser['username']}\n";
echo " 是否管理员: " . ($newUser['is_admin'] ? '是' : '否') . "\n";
} catch (Exception $e) {
echo " 创建用户失败: ✗ - " . $e->getMessage() . "\n";
}
}
// 测试5: 验证密码
echo "\n测试5: 验证密码\n";
$testUser = $userRepo->findByUsername($testUsername);
if ($testUser) {
$verified = $userRepo->verifyPassword($testUsername, 'test123456');
if ($verified) {
echo " 密码验证成功: ✓\n";
} else {
echo " 密码验证失败: ✗\n";
}
}
// 测试6: 获取所有用户
echo "\n测试6: 获取用户列表\n";
$users = $userRepo->getAll(10);
echo " 用户总数: " . count($users) . "\n";
foreach ($users as $user) {
echo " - {$user['username']} (ID: {$user['id']}, " .
($user['is_admin'] ? '管理员' : '普通用户') . ", " .
($user['is_active'] ? '激活' : '禁用') . ")\n";
}
echo "\n=== 测试完成 ===\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
echo "堆栈跟踪:\n" . $e->getTraceAsString() . "\n";
}

@ -1 +1,176 @@
//信件管理员" - 存信、取信
<?php
require_once __DIR__ . '/Database.php';
/**
* 邮件数据访问层
* 信件管理员 - 存信、取信
*/
class EmailRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 根据ID查找邮件
* @param int $id 邮件ID
* @return array|null 邮件信息或null
*/
public function findById($id) {
$stmt = $this->db->prepare("
SELECT e.*,
u1.username as sender_username,
u2.username as recipient_username
FROM emails e
LEFT JOIN users u1 ON e.sender_id = u1.id
LEFT JOIN users u2 ON e.recipient_id = u2.id
WHERE e.id = ? AND e.is_deleted = 0
");
$stmt->execute([$id]);
return $stmt->fetch();
}
/**
* 获取用户的收件箱邮件
* @param int $userId 用户ID
* @param int $limit 限制数量
* @param int $offset 偏移量
* @return array 邮件列表
*/
public function getInbox($userId, $limit = null, $offset = 0) {
$sql = "
SELECT e.*,
COALESCE(u1.username, e.sender) as sender_name,
COALESCE(u2.username, e.recipient) as recipient_name
FROM emails e
LEFT JOIN users u1 ON e.sender_id = u1.id
LEFT JOIN users u2 ON e.recipient_id = u2.id
WHERE (e.recipient_id = ? OR e.recipient = (SELECT username FROM users WHERE id = ?))
AND e.is_deleted = 0
ORDER BY e.created_at DESC
";
if ($limit !== null) {
$sql .= " LIMIT ? OFFSET ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $userId, $limit, $offset]);
} else {
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $userId]);
}
return $stmt->fetchAll();
}
/**
* 获取用户的发件箱邮件
* @param int $userId 用户ID
* @param int $limit 限制数量
* @param int $offset 偏移量
* @return array 邮件列表
*/
public function getSent($userId, $limit = null, $offset = 0) {
$sql = "
SELECT e.*,
COALESCE(u1.username, e.sender) as sender_name,
COALESCE(u2.username, e.recipient) as recipient_name
FROM emails e
LEFT JOIN users u1 ON e.sender_id = u1.id
LEFT JOIN users u2 ON e.recipient_id = u2.id
WHERE (e.sender_id = ? OR e.sender = (SELECT username FROM users WHERE id = ?))
AND e.is_deleted = 0
ORDER BY e.created_at DESC
";
if ($limit !== null) {
$sql .= " LIMIT ? OFFSET ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $userId, $limit, $offset]);
} else {
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $userId]);
}
return $stmt->fetchAll();
}
/**
* 获取所有邮件(管理员功能)
* @param int $limit 限制数量
* @param int $offset 偏移量
* @return array 邮件列表
*/
public function getAll($limit = null, $offset = 0) {
$sql = "
SELECT e.*,
COALESCE(u1.username, e.sender) as sender_name,
COALESCE(u2.username, e.recipient) as recipient_name
FROM emails e
LEFT JOIN users u1 ON e.sender_id = u1.id
LEFT JOIN users u2 ON e.recipient_id = u2.id
WHERE e.is_deleted = 0
ORDER BY e.created_at DESC
";
if ($limit !== null) {
$sql .= " LIMIT ? OFFSET ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$limit, $offset]);
} else {
$stmt = $this->db->query($sql);
}
return $stmt->fetchAll();
}
/**
* 获取邮件总数
* @param int|null $userId 用户ID如果提供只统计该用户的邮件
* @return int 邮件总数
*/
public function getCount($userId = null) {
if ($userId !== null) {
$stmt = $this->db->prepare("
SELECT COUNT(*) as count FROM emails
WHERE (recipient_id = ? OR sender_id = ?) AND is_deleted = 0
");
$stmt->execute([$userId, $userId]);
} else {
$stmt = $this->db->query("SELECT COUNT(*) as count FROM emails WHERE is_deleted = 0");
}
$result = $stmt->fetch();
return (int)$result['count'];
}
/**
* 标记邮件为已读
* @param int $id 邮件ID
* @return bool 是否成功
*/
public function markAsRead($id) {
$stmt = $this->db->prepare("UPDATE emails SET is_read = 1 WHERE id = ?");
return $stmt->execute([$id]);
}
/**
* 删除邮件(软删除)
* @param int $id 邮件ID
* @return bool 是否成功
*/
public function delete($id) {
$stmt = $this->db->prepare("UPDATE emails SET is_deleted = 1 WHERE id = ?");
return $stmt->execute([$id]);
}
/**
* 永久删除邮件
* @param int $id 邮件ID
* @return bool 是否成功
*/
public function permanentDelete($id) {
$stmt = $this->db->prepare("DELETE FROM emails WHERE id = ?");
return $stmt->execute([$id]);
}
}

@ -1 +1,173 @@
//查客户信息
<?php
require_once __DIR__ . '/Database.php';
require_once __DIR__ . '/../utils/Security.php';
/**
* 用户数据访问层
* 查客户信息、创建用户等
*/
class UserRepository {
private $db;
public function __construct() {
$this->db = Database::getInstance();
}
/**
* 根据用户名查找用户
* @param string $username 用户名(邮箱)
* @return array|null 用户信息或null
*/
public function findByUsername($username) {
$stmt = $this->db->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
return $stmt->fetch();
}
/**
* 根据ID查找用户
* @param int $id 用户ID
* @return array|null 用户信息或null
*/
public function findById($id) {
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
/**
* 检查用户名是否已存在
* @param string $username 用户名
* @return bool 是否存在
*/
public function usernameExists($username) {
$user = $this->findByUsername($username);
return $user !== false;
}
/**
* 创建新用户
* @param string $username 用户名(邮箱)
* @param string $password 明文密码
* @param bool $isAdmin 是否为管理员
* @param bool $isActive 是否激活
* @return array 创建的用户信息
* @throws Exception 如果创建失败
*/
public function create($username, $password, $isAdmin = false, $isActive = true) {
// 检查用户名是否已存在
if ($this->usernameExists($username)) {
throw new Exception("用户名已存在");
}
// 加密密码
$passwordHash = Security::hashPassword($password);
// 插入数据库
$stmt = $this->db->prepare("
INSERT INTO users (username, password_hash, is_admin, is_active, created_at)
VALUES (?, ?, ?, ?, NOW())
");
$stmt->execute([$username, $passwordHash, $isAdmin ? 1 : 0, $isActive ? 1 : 0]);
// 返回创建的用户信息
$userId = $this->db->lastInsertId();
return $this->findById($userId);
}
/**
* 更新用户信息
* @param int $id 用户ID
* @param array $data 要更新的数据 ['password' => string, 'is_admin' => bool, 'is_active' => bool]
* @return bool 是否成功
*/
public function update($id, $data) {
$updates = [];
$params = [];
if (isset($data['password'])) {
$updates[] = "password_hash = ?";
$params[] = Security::hashPassword($data['password']);
}
if (isset($data['is_admin'])) {
$updates[] = "is_admin = ?";
$params[] = $data['is_admin'] ? 1 : 0;
}
if (isset($data['is_active'])) {
$updates[] = "is_active = ?";
$params[] = $data['is_active'] ? 1 : 0;
}
if (empty($updates)) {
return false;
}
$params[] = $id;
$sql = "UPDATE users SET " . implode(", ", $updates) . " WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute($params);
}
/**
* 删除用户
* @param int $id 用户ID
* @return bool 是否成功
*/
public function delete($id) {
$stmt = $this->db->prepare("DELETE FROM users WHERE id = ?");
return $stmt->execute([$id]);
}
/**
* 获取所有用户列表
* @param int $limit 限制数量
* @param int $offset 偏移量
* @return array 用户列表
*/
public function getAll($limit = null, $offset = 0) {
$sql = "SELECT id, username, is_admin, is_active, created_at FROM users ORDER BY created_at DESC";
if ($limit !== null) {
$sql .= " LIMIT ? OFFSET ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$limit, $offset]);
} else {
$stmt = $this->db->query($sql);
}
return $stmt->fetchAll();
}
/**
* 获取用户总数
* @return int 用户总数
*/
public function getCount() {
$stmt = $this->db->query("SELECT COUNT(*) as count FROM users");
$result = $stmt->fetch();
return (int)$result['count'];
}
/**
* 验证用户密码
* @param string $username 用户名
* @param string $password 明文密码
* @return array|null 用户信息或null如果验证失败
*/
public function verifyPassword($username, $password) {
$user = $this->findByUsername($username);
if (!$user) {
return null;
}
if (!Security::verifyPassword($password, $user['password_hash'])) {
return null;
}
return $user;
}
}

@ -1 +1,129 @@
//加密密码、防攻击
<?php
/**
* 安全工具类
* 提供密码加密、防攻击等功能
*/
class Security {
/**
* 加密密码
* @param string $password 明文密码
* @return string 加密后的密码哈希
*/
public static function hashPassword($password) {
return password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
}
/**
* 验证密码
* @param string $password 明文密码
* @param string $hash 密码哈希
* @return bool 是否匹配
*/
public static function verifyPassword($password, $hash) {
return password_verify($password, $hash);
}
/**
* 清理输入防止XSS攻击
* @param string $input 用户输入
* @return string 清理后的字符串
*/
public static function sanitizeInput($input) {
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8');
}
/**
* 生成CSRF令牌
* @return string CSRF令牌
*/
public static function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* 验证CSRF令牌
* @param string $token 待验证的令牌
* @return bool 是否有效
*/
public static function verifyCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
/**
* 获取客户端IP地址
* @return string IP地址
*/
public static function getClientIP() {
$ipKeys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
foreach ($ipKeys as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
/**
* 防止暴力破解:检查登录尝试次数
* @param string $username 用户名
* @param int $maxAttempts 最大尝试次数
* @param int $lockoutTime 锁定时间(秒)
* @return bool 是否允许登录
*/
public static function checkLoginAttempts($username, $maxAttempts = 5, $lockoutTime = 300) {
$key = 'login_attempts_' . md5($username);
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
return true;
}
$attempts = $_SESSION[$key];
// 如果超过锁定时间,重置计数
if (time() - $attempts['time'] > $lockoutTime) {
$_SESSION[$key] = ['count' => 0, 'time' => time()];
return true;
}
// 检查是否超过最大尝试次数
if ($attempts['count'] >= $maxAttempts) {
return false;
}
return true;
}
/**
* 记录登录失败尝试
* @param string $username 用户名
*/
public static function recordLoginAttempt($username) {
$key = 'login_attempts_' . md5($username);
if (!isset($_SESSION[$key])) {
$_SESSION[$key] = ['count' => 1, 'time' => time()];
} else {
$_SESSION[$key]['count']++;
$_SESSION[$key]['time'] = time();
}
}
/**
* 清除登录尝试记录
* @param string $username 用户名
*/
public static function clearLoginAttempts($username) {
$key = 'login_attempts_' . md5($username);
unset($_SESSION[$key]);
}
}

@ -1 +1,157 @@
//检查输入是否合法
<?php
/**
* 输入验证工具类
* 检查输入是否合法
*/
class Validator {
/**
* 验证邮箱格式
* @param string $email 邮箱地址
* @return bool 是否有效
*/
public static function validateEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
/**
* 验证邮箱域名(检查是否属于指定域名)
* @param string $email 邮箱地址
* @param string $domain 允许的域名test.com
* @return bool 是否属于指定域名
*/
public static function validateEmailDomain($email, $domain = 'test.com') {
if (!self::validateEmail($email)) {
return false;
}
$emailDomain = substr(strrchr($email, "@"), 1);
return strtolower($emailDomain) === strtolower($domain);
}
/**
* 验证密码强度
* @param string $password 密码
* @param int $minLength 最小长度
* @return array ['valid' => bool, 'errors' => array] 验证结果和错误信息
*/
public static function validatePassword($password, $minLength = 6) {
$errors = [];
if (strlen($password) < $minLength) {
$errors[] = "密码长度至少需要 {$minLength} 个字符";
}
if (preg_match('/^[a-zA-Z0-9]+$/', $password) && strlen($password) < 8) {
// 如果密码只包含字母和数字且长度小于8建议使用更复杂的密码
// 但不强制要求
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 验证用户名格式
* @param string $username 用户名(邮箱格式)
* @return array ['valid' => bool, 'errors' => array] 验证结果和错误信息
*/
public static function validateUsername($username) {
$errors = [];
if (empty($username)) {
$errors[] = "用户名不能为空";
} elseif (!self::validateEmail($username)) {
$errors[] = "用户名必须是有效的邮箱格式";
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 验证IP地址格式
* @param string $ip IP地址
* @return bool 是否有效
*/
public static function validateIP($ip) {
return filter_var($ip, FILTER_VALIDATE_IP) !== false;
}
/**
* 验证端口号
* @param int $port 端口号
* @return bool 是否有效1-65535
*/
public static function validatePort($port) {
return is_numeric($port) && $port >= 1 && $port <= 65535;
}
/**
* 验证非空字符串
* @param string $value 待验证的值
* @param string $fieldName 字段名称(用于错误提示)
* @return array ['valid' => bool, 'errors' => array] 验证结果和错误信息
*/
public static function validateRequired($value, $fieldName = '字段') {
$errors = [];
if (empty(trim($value))) {
$errors[] = "{$fieldName}不能为空";
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 验证字符串长度
* @param string $value 待验证的值
* @param int $min 最小长度
* @param int $max 最大长度
* @param string $fieldName 字段名称
* @return array ['valid' => bool, 'errors' => array] 验证结果和错误信息
*/
public static function validateLength($value, $min, $max, $fieldName = '字段') {
$errors = [];
$length = mb_strlen($value, 'UTF-8');
if ($length < $min) {
$errors[] = "{$fieldName}长度不能少于 {$min} 个字符";
}
if ($length > $max) {
$errors[] = "{$fieldName}长度不能超过 {$max} 个字符";
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
/**
* 验证两个密码是否匹配
* @param string $password 密码
* @param string $confirmPassword 确认密码
* @return array ['valid' => bool, 'errors' => array] 验证结果和错误信息
*/
public static function validatePasswordMatch($password, $confirmPassword) {
$errors = [];
if ($password !== $confirmPassword) {
$errors[] = "两次输入的密码不一致";
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
}

Loading…
Cancel
Save