文件
2026-06-04 22:42:27 +08:00

674 行
22 KiB
PHP

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
<?php
// 引入数据库模块
require_once __DIR__ . '/../db.php';
// 生成ID
function generateId() {
return substr(md5(uniqid(mt_rand(), true)), 0, 13);
}
// 获取当前时间戳
function getCurrentTimestamp() {
return time();
}
// 生成随机验证码
function generateVerificationCode() {
return substr(md5(uniqid(mt_rand(), true)), 0, 32);
}
// 注册新用户
function register($username, $password, $email) {
$db = Database::getInstance();
// 检查用户名是否已存在
$result = $db->query("SELECT * FROM users WHERE username = :username", ['username' => $username]);
if (!empty($result)) {
return false;
}
// 检查邮箱是否已存在
$result = $db->query("SELECT * FROM users WHERE email = :email", ['email' => $email]);
if (!empty($result)) {
return false;
}
// 检查用户数量,第一个用户设为管理员
$userCount = $db->query("SELECT COUNT(*) as count FROM users");
$isAdmin = empty($userCount) || $userCount[0]['count'] == 0 ? 1 : 0;
// 生成验证码
$verificationCode = generateVerificationCode();
// 创建新用户
$newUser = [
'id' => generateId(),
'username' => $username,
'email' => $email,
'password' => password_hash($password, PASSWORD_DEFAULT),
'created_at' => getCurrentTimestamp(),
'last_login' => getCurrentTimestamp(),
'online' => 1,
'is_admin' => $isAdmin,
'is_banned' => 0,
'email_verified' => 0,
'verification_code' => $verificationCode
];
// 插入新用户
$sql = "INSERT INTO users (id, username, email, password, created_at, last_login, online, avatar, is_admin, is_banned, email_verified, verification_code)
VALUES (:id, :username, :email, :password, :created_at, :last_login, :online, '', :is_admin, :is_banned, :email_verified, :verification_code)";
$db->execute($sql, $newUser);
// 发送验证邮件
sendVerificationEmail($email, $verificationCode, $username);
return true;
}
// 用户登录
function login($username, $password, $rememberMe = false) {
$db = Database::getInstance();
// 获取用户
$result = $db->query("SELECT * FROM users WHERE username = :username", ['username' => $username]);
if (empty($result)) {
return false;
}
$user = $result[0];
// 检查用户是否被封禁
if ($user['is_banned'] === 1) {
return false;
}
// 验证密码
// 支持两种验证方式:
// 1. 直接验证(原始密码)- 使用 password_verify
// 2. SHA-256 哈希验证(前端已加密)- 检查是否是64位十六进制字符串
$passwordVerified = false;
// 检查密码是否是 SHA-256 哈希格式(64个十六进制字符)
$isSha256 = (strlen($password) === 64 && ctype_xdigit($password));
// 方案1:如果是 SHA-256 格式,直接比较(数据库存储的是 SHA-256 哈希)
if ($isSha256 && $user['password'] === $password) {
$passwordVerified = true;
}
// 方案2:尝试传统的 password_verify(兼容旧用户)
if (!$passwordVerified && password_verify($password, $user['password'])) {
$passwordVerified = true;
}
// 方案3:如果是 SHA-256,尝试用它来验证 bcrypt(新用户注册时使用 SHA-256 的 bcrypt
if (!$passwordVerified && $isSha256) {
// 尝试用 SHA-256 哈希去匹配 bcrypt
if (password_verify($password, $user['password'])) {
$passwordVerified = true;
}
}
if (!$passwordVerified) {
return false;
}
// 获取用户真实IP
function getRealIP() {
$ip = $_SERVER['REMOTE_ADDR'];
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$ip = trim($ips[0]);
}
return $ip;
}
// 更新用户在线状态、最后登录时间和IP地址
$sql = "UPDATE users SET online = 1, last_login = :last_login, last_ip = :last_ip WHERE id = :id";
$params = [
'last_login' => getCurrentTimestamp(),
'last_ip' => getRealIP(),
'id' => $user['id']
];
$db->execute($sql, $params);
// 设置会话
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
// 如果用户选择"记住我",创建记住我令牌
if ($rememberMe) {
createRememberMeToken($user['id']);
}
return true;
}
// 生成记住我令牌
function createRememberMeToken($userId) {
// 生成随机令牌
$token = bin2hex(random_bytes(32));
// 设置过期时间(30天)
$expires = getCurrentTimestamp() + (30 * 24 * 60 * 60);
$db = Database::getInstance();
// 更新用户的记住我令牌
$db->execute("UPDATE users SET remember_token = :token, remember_token_expires = :expires WHERE id = :user_id", [
'token' => $token,
'expires' => $expires,
'user_id' => $userId
]);
// 设置cookie30天过期)
setcookie('remember_token', $token, $expires, '/', '', false, true);
}
// 清除记住我令牌
function clearRememberMeToken() {
// 清除cookie
setcookie('remember_token', '', time() - 3600, '/', '', false, true);
// 如果用户已登录,清除数据库中的令牌
if (isLoggedIn()) {
$db = Database::getInstance();
$db->execute("UPDATE users SET remember_token = '', remember_token_expires = 0 WHERE id = :user_id", [
'user_id' => $_SESSION['user_id']
]);
}
}
// 通过记住我令牌自动登录
function autoLoginWithRememberToken() {
// 如果已经登录,不需要自动登录
if (isLoggedIn()) {
return true;
}
// 检查是否有记住我cookie
if (!isset($_COOKIE['remember_token'])) {
return false;
}
$token = $_COOKIE['remember_token'];
$db = Database::getInstance();
// 查询用户
$result = $db->query("SELECT * FROM users WHERE remember_token = :token AND remember_token_expires > :now", [
'token' => $token,
'now' => getCurrentTimestamp()
]);
if (empty($result)) {
// 令牌无效或过期,清除cookie
clearRememberMeToken();
return false;
}
$user = $result[0];
// 检查用户是否被封禁
if ($user['is_banned'] === 1) {
clearRememberMeToken();
return false;
}
// 设置会话
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
// 更新在线状态
$db->execute("UPDATE users SET online = 1, last_login = :last_login WHERE id = :id", [
'last_login' => getCurrentTimestamp(),
'id' => $user['id']
]);
return true;
}
// 用户登出
function logout() {
if (isLoggedIn()) {
$db = Database::getInstance();
// 更新用户在线状态
$sql = "UPDATE users SET online = 0 WHERE id = :id";
$db->execute($sql, ['id' => $_SESSION['user_id']]);
// 清除记住我令牌
clearRememberMeToken();
// 销毁会话
session_destroy();
}
}
// 检查用户是否已登录
function isLoggedIn() {
return isset($_SESSION['user_id']) && isset($_SESSION['username']);
}
// 获取当前登录用户信息
function getCurrentUser() {
if (!isLoggedIn()) {
return null;
}
return [
'id' => $_SESSION['user_id'],
'username' => $_SESSION['username']
];
}
// 更新用户在线状态
function updateUserOnlineStatus($userId, $online) {
$db = Database::getInstance();
$sql = "UPDATE users SET online = :online WHERE id = :id";
$db->execute($sql, [
'online' => $online ? 1 : 0,
'id' => $userId
]);
}
// 获取用户在线状态
function getUserOnlineStatus($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT online FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return false;
}
return $result[0]['online'] === 1;
}
// 获取用户当前头像
function getCurrentUserAvatar($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT avatar FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return '';
}
return $result[0]['avatar'] ?? '';
}
// 更新用户头像
function updateUserAvatar($userId, $avatarPath) {
$db = Database::getInstance();
$sql = "UPDATE users SET avatar = :avatar WHERE id = :id";
return $db->execute($sql, [
'avatar' => $avatarPath,
'id' => $userId
]);
}
// 更改用户名
function changeUsername($userId, $newUsername) {
$db = Database::getInstance();
// 检查新用户名是否已被其他用户使用
$result = $db->query("SELECT * FROM users WHERE username = :username AND id != :id", [
'username' => $newUsername,
'id' => $userId
]);
if (!empty($result)) {
return false;
}
// 更新当前用户的用户名
$sql = "UPDATE users SET username = :username WHERE id = :id";
return $db->execute($sql, [
'username' => $newUsername,
'id' => $userId
]);
}
// 根据用户名获取用户
function getUserByUsername($username) {
$db = Database::getInstance();
$result = $db->query("SELECT * FROM users WHERE username = :username", ['username' => $username]);
return !empty($result) ? $result[0] : false;
}
// 根据ID获取用户
function getUserById($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT * FROM users WHERE id = :id", ['id' => $userId]);
return !empty($result) ? $result[0] : false;
}
// 获取所有用户
function getAllUsers() {
$db = Database::getInstance();
return $db->query("SELECT * FROM users");
}
// 检查用户是否是管理员
function isAdmin($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT is_admin FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return false;
}
return $result[0]['is_admin'] === 1;
}
// 检查用户是否被封禁
function isBanned($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT is_banned FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return false;
}
return $result[0]['is_banned'] === 1;
}
// 封禁用户
function banUser($userId) {
$db = Database::getInstance();
$sql = "UPDATE users SET is_banned = 1 WHERE id = :id";
return $db->execute($sql, ['id' => $userId]);
}
// 解除用户封禁
function unbanUser($userId) {
$db = Database::getInstance();
$sql = "UPDATE users SET is_banned = 0 WHERE id = :id";
return $db->execute($sql, ['id' => $userId]);
}
// 修改用户密码
function changeUserPassword($userId, $newPassword) {
$db = Database::getInstance();
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
$sql = "UPDATE users SET password = :password WHERE id = :id";
return $db->execute($sql, [
'password' => $hashedPassword,
'id' => $userId
]);
}
// 修改用户简介
function changeUserBio($userId, $newBio) {
$db = Database::getInstance();
$sql = "UPDATE users SET bio = :bio WHERE id = :id";
return $db->execute($sql, [
'bio' => $newBio,
'id' => $userId
]);
}
// 设置用户管理员权限
function setAdmin($userId, $isAdmin) {
$db = Database::getInstance();
$sql = "UPDATE users SET is_admin = :is_admin WHERE id = :id";
return $db->execute($sql, [
'is_admin' => $isAdmin ? 1 : 0,
'id' => $userId
]);
}
// 记录邮箱操作日志
function logEmailOperation($operation, $userId, $email, $details = '') {
$logMessage = date('Y-m-d H:i:s') . " - [{$operation}] User ID: {$userId}, Email: {$email}, Details: {$details}\n";
$logFile = __DIR__ . '/../logs/email_operations.log';
// 确保日志目录存在
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
error_log($logMessage, 3, $logFile);
}
// 发送验证邮件
function sendVerificationEmail($email, $verificationCode, $username, $userId = '') {
// 引入PHPMailer
require_once __DIR__ . '/../vendor/autoload.php';
// 创建PHPMailer实例
$mail = new PHPMailer\PHPMailer\PHPMailer();
try {
// 服务器设置
$mail->isSMTP();
$mail->Host = 'smtp.qiye.163.com'; // 网易企业邮SMTP服务器
$mail->SMTPAuth = true;
$mail->Username = 'noreply@parlz.com'; // SMTP用户名
$mail->Password = '#5q8HhphvBCWr4n8'; // SMTP授权码
$mail->SMTPSecure = 'tls'; // 使用STARTTLS加密
$mail->Port = 587; // STARTTLS端口
// 设置超时时间
$mail->Timeout = 30;
// 添加SSL选项(禁用证书验证)
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->setLanguage('zh_CN', __DIR__ . '/../vendor/phpmailer/phpmailer/language/');
$mail->SMTPDebug = 0; // 关闭调试模式
// 发件人(必须与SMTP用户名一致)
$mail->setFrom('noreply@parlz.com', 'Parlz');
// 收件人
$mail->addAddress($email, $username);
// 邮件内容
$mail->isHTML(true);
$mail->CharSet = 'UTF-8'; // 设置字符集为UTF-8
$mail->Encoding = 'base64'; // 使用base64编码
$mail->Subject = '邮箱验证 - Parlz';
$mail->Body = "
<html>
<body style='font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif; background-color: #f5f5f5; color: #333; line-height: 1.6; margin: 0; padding: 20px;'>
<div style='max-width: 600px; margin: 0 auto; background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); padding: 30px;'>
<div style='text-align: center; margin-bottom: 30px;'>
<h1 style='color: #07c160; font-size: 28px; font-weight: bold; margin: 0;'>Parlz</h1>
<p style='color: #666; margin: 10px 0 0;'>实时聊天应用</p>
</div>
<div style='border-top: 1px solid #eee; padding-top: 20px;'>
<h2 style='color: #333; font-size: 20px; margin-bottom: 20px;'>欢迎使用Parlz!</h2>
<p style='margin-bottom: 15px;'>尊敬的 {$username},</p>
<p style='margin-bottom: 20px;'>感谢您注册Parlz账号!为了确保您的账号安全并解锁全部功能,请点击下方按钮验证您的邮箱:</p>
<div style='text-align: center; margin: 30px 0;'>
<a href='https://www.parlz.com/index.php?action=verify_email&code={$verificationCode}' style='display: inline-block; background-color: #07c160; color: white; text-decoration: none; padding: 12px 30px; border-radius: 25px; font-size: 16px; font-weight: bold; transition: all 0.3s ease;'>
点击验证邮箱
</a>
</div>
<p style='margin-bottom: 15px;'>如果按钮无法点击,请复制以下链接到浏览器地址栏访问:</p>
<p style='background-color: #f8f8f8; padding: 15px; border-radius: 5px; word-break: break-all; font-family: monospace; font-size: 14px;'>
https://www.parlz.com/index.php?action=verify_email&code={$verificationCode}
</p>
<p style='margin-top: 20px;'>验证邮箱后,您将可以:</p>
<ul style='margin: 15px 0 20px 20px;'>
<li style='margin-bottom: 8px;'>添加好友</li>
<li style='margin-bottom: 8px;'>创建群组</li>
<li style='margin-bottom: 8px;'>加入群组</li>
<li style='margin-bottom: 8px;'>发送和接收消息</li>
</ul>
<p style='margin-bottom: 15px;'>如果您没有注册Parlz账号,请忽略此邮件。</p>
</div>
<div style='border-top: 1px solid #eee; margin-top: 30px; padding-top: 20px; text-align: center;'>
<p style='color: #666; font-size: 14px;'>此致,<br>Parlz团队</p>
<p style='color: #999; font-size: 12px; margin-top: 10px;'>
此邮件由系统自动发送,请勿直接回复。<br>
© 2026 Parlz. 保留所有权利。
</p>
</div>
</div>
</body>
</html>
";
// 发送邮件
$success = $mail->send();
// 检查发送结果 - 使用isError()方法更准确
if ($mail->isError()) {
throw new Exception($mail->ErrorInfo);
}
// 记录邮件发送成功日志
if (empty($userId)) {
// 尝试获取用户ID
$user = getUserByUsername($username);
if ($user) {
$userId = $user['id'];
}
}
logEmailOperation('SEND_VERIFICATION_EMAIL', $userId, $email, 'Verification code: ' . $verificationCode);
return true;
} catch (Exception $e) {
// 邮件发送失败,记录错误
$errorMessage = '邮件发送失败: ' . $e->getMessage();
error_log($errorMessage);
// 记录邮件发送失败日志
if (empty($userId)) {
// 尝试获取用户ID
$user = getUserByUsername($username);
if ($user) {
$userId = $user['id'];
}
}
logEmailOperation('SEND_VERIFICATION_EMAIL_ERROR', $userId, $email, $e->getMessage());
return false;
}
}
// 验证邮箱
function verifyEmail($verificationCode) {
$db = Database::getInstance();
// 查找验证代码对应的用户
$result = $db->query("SELECT * FROM users WHERE verification_code = :code", ['code' => $verificationCode]);
if (empty($result)) {
// 记录验证失败日志
logEmailOperation('VERIFY_EMAIL_FAILED', '', '', 'Invalid verification code: ' . $verificationCode);
return false;
}
$user = $result[0];
// 更新用户邮箱验证状态
$sql = "UPDATE users SET email_verified = 1, verification_code = '' WHERE id = :id";
$success = $db->execute($sql, ['id' => $user['id']]);
// 记录验证结果日志
if ($success) {
logEmailOperation('VERIFY_EMAIL_SUCCESS', $user['id'], $user['email'], 'Verification code: ' . $verificationCode);
} else {
logEmailOperation('VERIFY_EMAIL_ERROR', $user['id'], $user['email'], 'Failed to update verification status');
}
return $success;
}
// 检查用户邮箱是否已验证
function isEmailVerified($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT email_verified FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return false;
}
return $result[0]['email_verified'] == 1;
}
// 获取用户邮箱
function getUserEmail($userId) {
$db = Database::getInstance();
$result = $db->query("SELECT email FROM users WHERE id = :id", ['id' => $userId]);
if (empty($result)) {
return '';
}
return $result[0]['email'];
}
// 更新用户邮箱
function updateUserEmail($userId, $email) {
$db = Database::getInstance();
// 检查邮箱是否已被其他用户使用
$result = $db->query("SELECT * FROM users WHERE email = :email AND id != :id", [
'email' => $email,
'id' => $userId
]);
if (!empty($result)) {
// 记录邮箱更新失败日志
logEmailOperation('UPDATE_EMAIL_FAILED', $userId, $email, 'Email already in use');
return false;
}
// 生成新的验证码
$verificationCode = generateVerificationCode();
// 更新用户邮箱和验证码
$sql = "UPDATE users SET email = :email, email_verified = 0, verification_code = :code WHERE id = :id";
$result = $db->execute($sql, [
'email' => $email,
'code' => $verificationCode,
'id' => $userId
]);
// 记录邮箱更新结果日志
if ($result) {
logEmailOperation('UPDATE_EMAIL_SUCCESS', $userId, $email, 'New verification code generated');
// 发送验证邮件(非阻塞,通过设置超时)
$user = getUserById($userId);
if ($user) {
$emailSent = sendVerificationEmail($email, $verificationCode, $user['username'], $userId);
} else {
// 即使获取用户信息失败,也尝试发送邮件
$emailSent = sendVerificationEmail($email, $verificationCode, 'User', $userId);
// 记录警告日志
logEmailOperation('UPDATE_EMAIL_WARNING', $userId, $email, 'Failed to get user info, but email sent with default username');
}
// 如果邮件发送失败,返回false
if (!$emailSent) {
logEmailOperation('UPDATE_EMAIL_EMAIL_FAILED', $userId, $email, 'Failed to send verification email');
return false;
}
} else {
logEmailOperation('UPDATE_EMAIL_ERROR', $userId, $email, 'Failed to update email in database');
}
return $result;
}
?>