Files
chatroom/app/Http/Controllers/AuthController.php

208 lines
7.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能:认证控制器 (处理登录即注册等逻辑)
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Http\Controllers;
use App\Http\Requests\LoginRequest;
use App\Models\Sysparam;
use App\Models\User;
use App\Models\UsernameBlacklist;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Redis;
class AuthController extends Controller
{
/**
* 处理用户登录/注册尝试。
* 逻辑:
* 1. 如果用户已存在验证密码。为了兼容老数据库先验证Bcrypt再退化验证MD5。如果MD5正确则升级为Bcrypt。
* 2. 如果用户不存在,直接注册新用户并登录。
*/
public function login(LoginRequest $request): JsonResponse
{
$credentials = $request->validated();
$username = $credentials['username'];
$password = $credentials['password'];
$ip = $request->ip();
$user = User::where('username', $username)->first();
if ($user) {
// 用户存在,验证密码
if (Hash::check($password, $user->password)) {
// Bcrypt 验证通过
// 检测是否被封禁 (后台管理员级别获得豁免权,防止误把自己关在门外)
$adminLevel = (int) Sysparam::getValue('superlevel', '100');
if ($user->user_level < $adminLevel) {
if ($user->user_level < 0) {
return response()->json(['status' => 'error', 'message' => '您的账号已被管理员封禁,无法登录。'], 403);
}
if (Redis::sismember('banned_ips', $ip)) {
return response()->json(['status' => 'error', 'message' => '您所在的 IP 地址已被管理员封禁,限制访问。'], 403);
}
}
$this->performLogin($user, $ip);
return response()->json(['status' => 'success', 'message' => '登录成功']);
}
// 退化为 MD5 验证(兼容原 ASP 系统的老密码)
if (md5($password) === $user->password) {
// MD5 验证通过,升级密码为 Bcrypt
$user->password = Hash::make($password);
$user->save();
// 检测是否被封禁 (后台管理员级别获得豁免权,防止误把自己关在门外)
$adminLevel = (int) Sysparam::getValue('superlevel', '100');
if ($user->user_level < $adminLevel) {
if ($user->user_level < 0) {
return response()->json(['status' => 'error', 'message' => '您的账号已被管理员封禁,无法登录。'], 403);
}
if (Redis::sismember('banned_ips', $ip)) {
return response()->json(['status' => 'error', 'message' => '您所在的 IP 地址已被管理员封禁,限制访问。'], 403);
}
}
$this->performLogin($user, $ip);
return response()->json(['status' => 'success', 'message' => '登录成功,且安全策略已自动升级']);
}
// 密码错误
return response()->json([
'status' => 'error',
'message' => '密码错误,请重试。',
], 422);
}
// --- 核心:第一次登录即为注册 ---
// 映射性别1=男 2=女 0=保密(数据库 sex 列为 int
$sex = (int) $request->input('bSex', 0);
if (! in_array($sex, [1, 2])) {
$sex = 0;
}
// 新注册用户:只检测 IP 封禁 (新号不存在账号封禁)
if (Redis::sismember('banned_ips', $ip)) {
return response()->json(['status' => 'error', 'message' => '您所在的 IP 地址已被管理员封禁,禁止注册新账号。'], 403);
}
// 检测用户名是否在禁用词列表(永久禁用 或 改名临时保留期内)
if (UsernameBlacklist::isBlocked($username)) {
return response()->json(['status' => 'error', 'message' => '该用户名已被系统禁止注册,请更换其他名称。'], 422);
}
// --- 提取邀请人 Cookie ---
$inviterIdCookie = $request->cookie('inviter_id');
$inviterId = null;
if ($inviterIdCookie && is_numeric($inviterIdCookie)) {
// 简单校验邀请人是否存在,防止脏数据
if (User::where('id', $inviterIdCookie)->exists()) {
$inviterId = (int) $inviterIdCookie;
}
}
$newUser = User::create([
'username' => $username,
'password' => Hash::make($password),
'first_ip' => $ip,
'last_ip' => $ip,
'user_level' => 1, // 默认普通用户等级
'sex' => $sex,
'usersf' => '1.gif', // 默认头像
'inviter_id' => $inviterId, // 记录邀请人
]);
$this->performLogin($newUser, $ip);
// 如果是通过邀请注册的,响应成功后建议清除 Cookie防止污染后续注册
if ($inviterId) {
\Illuminate\Support\Facades\Cookie::queue(\Illuminate\Support\Facades\Cookie::forget('inviter_id'));
}
return response()->json(['status' => 'success', 'message' => '注册并登录成功!']);
}
/**
* 执行实际的登录操作并记录时间、IP 等。
*/
private function performLogin(User $user, string $ip): void
{
Auth::login($user);
// 递增访问次数
$user->increment('visit_num');
// 更新最后登录IP和时间同时将旧IP转移到 previous_ip 作上次登录记录)
$user->update([
'previous_ip' => $user->last_ip,
'last_ip' => $ip,
'log_time' => now(),
'in_time' => now(),
]);
// 可选:将用户登录状态也同步写入原有的 IpLog 模型,以便数据归档查询
\App\Models\IpLog::create([
'ip' => $ip,
'sdate' => now(),
'uuname' => $user->username,
]);
// 触发微信机器人消息推送 (登录上线类)
try {
$wechatService = new \App\Services\WechatBot\WechatNotificationService;
$wechatService->notifyAdminOnline($user);
$wechatService->notifyFriendsOnline($user);
$wechatService->notifySpouseOnline($user);
} catch (\Exception $e) {
\Illuminate\Support\Facades\Log::error('WechatBot presence notification failed', ['error' => $e->getMessage()]);
}
}
/**
* 退出登录,清除会话后跳转回登录首页
*/
public function logout(Request $request): \Illuminate\Http\RedirectResponse
{
if (Auth::check()) {
$user = Auth::user();
// 记录退出时间和退出信息
$user->update([
'out_time' => now(),
'out_info' => '正常退出了聊天室',
]);
// [NEW] 同步清除该用户在所有房间的在线状态和心跳,确保其如果马上重登,能触发全新入场欢迎
try {
$chatState = app(\App\Services\ChatStateService::class);
$roomIds = $chatState->getUserRooms($user->username);
foreach ($roomIds as $roomId) {
$chatState->userLeave($roomId, $user->username);
}
} catch (\Exception $e) {
// 忽略清理缓存时发生的异常
}
}
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/')->with('success', '您已成功退出聊天室,欢迎下次再来!');
}
}