2026-02-26 13:35:38 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 文件功能:认证控制器 (处理登录即注册等逻辑)
|
|
|
|
|
|
*
|
|
|
|
|
|
* @author ChatRoom Laravel
|
|
|
|
|
|
*
|
|
|
|
|
|
* @version 1.0.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
|
|
use App\Http\Requests\LoginRequest;
|
2026-03-01 14:00:38 +08:00
|
|
|
|
use App\Models\Sysparam;
|
2026-02-26 13:35:38 +08:00
|
|
|
|
use App\Models\User;
|
2026-03-01 14:00:38 +08:00
|
|
|
|
use App\Models\UsernameBlacklist;
|
2026-02-26 13:35:38 +08:00
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
|
|
use Illuminate\Support\Facades\Hash;
|
2026-02-27 09:30:14 +08:00
|
|
|
|
use Illuminate\Support\Facades\Redis;
|
2026-02-26 13:35:38 +08:00
|
|
|
|
|
|
|
|
|
|
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 验证通过
|
2026-02-27 09:30:14 +08:00
|
|
|
|
|
2026-02-27 09:34:24 +08:00
|
|
|
|
// 检测是否被封禁 (后台管理员级别获得豁免权,防止误把自己关在门外)
|
|
|
|
|
|
$adminLevel = (int) Sysparam::getValue('superlevel', '100');
|
|
|
|
|
|
if ($user->user_level < $adminLevel) {
|
2026-02-27 09:30:14 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 13:35:38 +08:00
|
|
|
|
$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();
|
|
|
|
|
|
|
2026-02-27 09:34:24 +08:00
|
|
|
|
// 检测是否被封禁 (后台管理员级别获得豁免权,防止误把自己关在门外)
|
|
|
|
|
|
$adminLevel = (int) Sysparam::getValue('superlevel', '100');
|
|
|
|
|
|
if ($user->user_level < $adminLevel) {
|
2026-02-27 09:30:14 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 13:35:38 +08:00
|
|
|
|
$this->performLogin($user, $ip);
|
|
|
|
|
|
|
|
|
|
|
|
return response()->json(['status' => 'success', 'message' => '登录成功,且安全策略已自动升级']);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 密码错误
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
|
'status' => 'error',
|
|
|
|
|
|
'message' => '密码错误,请重试。',
|
|
|
|
|
|
], 422);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- 核心:第一次登录即为注册 ---
|
|
|
|
|
|
|
2026-02-26 22:57:30 +08:00
|
|
|
|
// 映射性别:1=男 2=女 0=保密(数据库 sex 列为 int)
|
|
|
|
|
|
$sex = (int) $request->input('bSex', 0);
|
|
|
|
|
|
if (! in_array($sex, [1, 2])) {
|
|
|
|
|
|
$sex = 0;
|
|
|
|
|
|
}
|
2026-02-26 22:50:35 +08:00
|
|
|
|
|
2026-02-27 09:30:14 +08:00
|
|
|
|
// 新注册用户:只检测 IP 封禁 (新号不存在账号封禁)
|
|
|
|
|
|
if (Redis::sismember('banned_ips', $ip)) {
|
|
|
|
|
|
return response()->json(['status' => 'error', 'message' => '您所在的 IP 地址已被管理员封禁,禁止注册新账号。'], 403);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 14:00:38 +08:00
|
|
|
|
// 检测用户名是否在禁用词列表(永久禁用 或 改名临时保留期内)
|
|
|
|
|
|
if (UsernameBlacklist::isBlocked($username)) {
|
|
|
|
|
|
return response()->json(['status' => 'error', 'message' => '该用户名已被系统禁止注册,请更换其他名称。'], 422);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-12 09:33:38 +08:00
|
|
|
|
// --- 提取邀请人 Cookie ---
|
|
|
|
|
|
$inviterIdCookie = $request->cookie('inviter_id');
|
|
|
|
|
|
$inviterId = null;
|
|
|
|
|
|
if ($inviterIdCookie && is_numeric($inviterIdCookie)) {
|
|
|
|
|
|
// 简单校验邀请人是否存在,防止脏数据
|
|
|
|
|
|
if (User::where('id', $inviterIdCookie)->exists()) {
|
|
|
|
|
|
$inviterId = (int) $inviterIdCookie;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 13:35:38 +08:00
|
|
|
|
$newUser = User::create([
|
|
|
|
|
|
'username' => $username,
|
|
|
|
|
|
'password' => Hash::make($password),
|
|
|
|
|
|
'first_ip' => $ip,
|
|
|
|
|
|
'last_ip' => $ip,
|
|
|
|
|
|
'user_level' => 1, // 默认普通用户等级
|
2026-02-26 22:50:35 +08:00
|
|
|
|
'sex' => $sex,
|
2026-02-26 23:27:35 +08:00
|
|
|
|
'usersf' => '1.gif', // 默认头像
|
2026-03-12 09:33:38 +08:00
|
|
|
|
'inviter_id' => $inviterId, // 记录邀请人
|
2026-02-26 13:35:38 +08:00
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$this->performLogin($newUser, $ip);
|
|
|
|
|
|
|
2026-03-12 09:33:38 +08:00
|
|
|
|
// 如果是通过邀请注册的,响应成功后建议清除 Cookie,防止污染后续注册
|
|
|
|
|
|
if ($inviterId) {
|
|
|
|
|
|
\Illuminate\Support\Facades\Cookie::queue(\Illuminate\Support\Facades\Cookie::forget('inviter_id'));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 13:35:38 +08:00
|
|
|
|
return response()->json(['status' => 'success', 'message' => '注册并登录成功!']);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 执行实际的登录操作并记录时间、IP 等。
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function performLogin(User $user, string $ip): void
|
|
|
|
|
|
{
|
|
|
|
|
|
Auth::login($user);
|
|
|
|
|
|
|
2026-02-27 10:11:09 +08:00
|
|
|
|
// 递增访问次数
|
|
|
|
|
|
$user->increment('visit_num');
|
2026-03-01 14:00:38 +08:00
|
|
|
|
|
2026-03-09 11:53:58 +08:00
|
|
|
|
// 更新最后登录IP和时间(同时将旧IP转移到 previous_ip 作上次登录记录)
|
2026-02-26 13:35:38 +08:00
|
|
|
|
$user->update([
|
2026-03-09 11:53:58 +08:00
|
|
|
|
'previous_ip' => $user->last_ip,
|
2026-02-26 13:35:38 +08:00
|
|
|
|
'last_ip' => $ip,
|
|
|
|
|
|
'log_time' => now(),
|
|
|
|
|
|
'in_time' => now(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 可选:将用户登录状态也同步写入原有的 IpLog 模型,以便数据归档查询
|
|
|
|
|
|
\App\Models\IpLog::create([
|
|
|
|
|
|
'ip' => $ip,
|
|
|
|
|
|
'sdate' => now(),
|
|
|
|
|
|
'uuname' => $user->username,
|
|
|
|
|
|
]);
|
2026-04-02 14:56:51 +08:00
|
|
|
|
|
|
|
|
|
|
// 触发微信机器人消息推送 (登录上线类)
|
|
|
|
|
|
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()]);
|
|
|
|
|
|
}
|
2026-02-26 13:35:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-02-26 14:57:24 +08:00
|
|
|
|
* 退出登录,清除会话后跳转回登录首页
|
2026-02-26 13:35:38 +08:00
|
|
|
|
*/
|
2026-02-26 14:57:24 +08:00
|
|
|
|
public function logout(Request $request): \Illuminate\Http\RedirectResponse
|
2026-02-26 13:35:38 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (Auth::check()) {
|
|
|
|
|
|
$user = Auth::user();
|
2026-02-27 10:11:09 +08:00
|
|
|
|
// 记录退出时间和退出信息
|
|
|
|
|
|
$user->update([
|
|
|
|
|
|
'out_time' => now(),
|
|
|
|
|
|
'out_info' => '正常退出了聊天室',
|
|
|
|
|
|
]);
|
2026-02-26 13:35:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Auth::logout();
|
|
|
|
|
|
|
|
|
|
|
|
$request->session()->invalidate();
|
|
|
|
|
|
$request->session()->regenerateToken();
|
|
|
|
|
|
|
2026-02-26 14:57:24 +08:00
|
|
|
|
return redirect('/')->with('success', '您已成功退出聊天室,欢迎下次再来!');
|
2026-02-26 13:35:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|