291 lines
10 KiB
PHP
291 lines
10 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\admin\controller;
|
||
|
||
use app\admin\BaseController;
|
||
use think\exception\ValidateException;
|
||
use think\facade\Db;
|
||
use think\facade\Cache;
|
||
use think\response\Json;
|
||
use app\service\JwtService;
|
||
|
||
use app\model\AdminUser;
|
||
use app\model\System\SystemSiteSettings;
|
||
use app\model\Tenant\Tenant;
|
||
|
||
class LoginController extends BaseController
|
||
{
|
||
private function generateToken($userInfo): string
|
||
{
|
||
return JwtService::generateToken($userInfo);
|
||
}
|
||
|
||
private function verifyToken($token): ?array
|
||
{
|
||
return JwtService::verifyToken($token);
|
||
}
|
||
|
||
/**
|
||
* 登录接口
|
||
* @return Json
|
||
*/
|
||
public function login(): Json
|
||
{
|
||
try {
|
||
$data = $this->request->param();
|
||
|
||
// 1. 先验证租户名称是否传入(新增:必须传租户名称)
|
||
try {
|
||
$this->validate($data, [
|
||
'tenant_name|租户名称' => 'require|length:1,128', // 匹配租户表tenant字段长度
|
||
'account|账号' => 'require|length:3,32',
|
||
'password|密码' => 'require|length:6,32'
|
||
]);
|
||
} catch (ValidateException $e) {
|
||
$this->logFail('登录管理', '登录', $e->getMessage());
|
||
return json([
|
||
'code' => 400,
|
||
'msg' => $e->getError()
|
||
]);
|
||
}
|
||
|
||
// 2. 处理账号兼容(邮箱/手机号转account)
|
||
if (isset($data['email'])) {
|
||
$data['account'] = $data['email'];
|
||
} elseif (isset($data['phone'])) {
|
||
$data['account'] = $data['phone'];
|
||
}
|
||
|
||
// 3. 查询租户ID(新增:只查正常状态的租户 status=1)
|
||
$tenant = Tenant::where('tenant_name', $data['tenant_name'])
|
||
->where('status', 1) // 过滤停用/删除的租户
|
||
->field(['id', 'tenant_name']) // 只查需要的字段,提升性能
|
||
->find(); // ThinkPHP 使用 find()
|
||
|
||
|
||
|
||
// 4. 验证租户是否存在
|
||
|
||
if (!$tenant) {
|
||
$this->logFail('登录管理', '登录', '租户不存在或已禁用,租户名称:' . $data['tenant_name']);
|
||
return json([
|
||
'code' => 401,
|
||
'msg' => '租户不存在或已禁用'
|
||
]);
|
||
}
|
||
$tenant_id = $tenant->id;
|
||
$tenant_name = $tenant->tenant_name;
|
||
|
||
// 5. 查询用户(新增:关联租户ID,确保用户属于该租户)
|
||
$user = AdminUser::where('account', $data['account'])
|
||
->where('tenant_id', $tenant_id) // 核心:验证用户所属租户
|
||
->where('status', 1)
|
||
->find();
|
||
|
||
// 6. 验证用户是否存在
|
||
if (!$user) {
|
||
$this->logFail('登录管理', '登录', '账号不存在/已禁用,或不属于当前租户,账号:' . $data['account'] . ',租户:' . $tenant_name);
|
||
return json([
|
||
'code' => 401,
|
||
'msg' => '账号不存在或已禁用,或不属于当前租户'
|
||
]);
|
||
}
|
||
|
||
// 7. 验证密码
|
||
if (md5($data['password']) !== $user['password']) {
|
||
$this->logFail('登录管理', '登录', '密码错误,账号:' . $data['account'] . ',租户:' . $tenant_name);
|
||
return json([
|
||
'code' => 401,
|
||
'msg' => '密码错误'
|
||
]);
|
||
}
|
||
|
||
// 8. 更新登录次数和IP
|
||
try {
|
||
$loginCount = isset($user['login_count']) && $user['login_count'] !== null ? (int)$user['login_count'] : 0;
|
||
AdminUser::where('id', $user['id'])->update([
|
||
'login_count' => $loginCount + 1,
|
||
'last_login_ip' => $this->request->ip(),
|
||
'last_login_time' => date('Y-m-d H:i:s') // 新增:记录最后登录时间,更实用
|
||
]);
|
||
} catch (\Exception $e) {
|
||
error_log('更新登录信息失败: ' . $e->getMessage());
|
||
}
|
||
|
||
// 9. 组装用户信息(新增:加入租户ID和租户名称)
|
||
$userInfo = [
|
||
'id' => $user['id'],
|
||
'account' => $user['account'],
|
||
'name' => $user['name'],
|
||
'group_id' => $user['group_id'],
|
||
'tenant_id' => $tenant_id, // 新增:租户ID
|
||
'tenant' => $tenant // 新增:租户名称
|
||
];
|
||
|
||
// 10. 生成Token(Token中已包含租户信息,后续可通过Token解析获取)
|
||
try {
|
||
$token = $this->generateToken($userInfo);
|
||
} catch (\Exception $e) {
|
||
$this->logFail('登录管理', '登录', 'Token生成失败: ' . $e->getMessage());
|
||
return json([
|
||
'code' => 500,
|
||
'msg' => '登录失败,请稍后重试'
|
||
]);
|
||
}
|
||
|
||
// 11. 写入用户数据缓存(核心:缓存包含租户信息,示例用Redis,可根据你的缓存工具调整)
|
||
try {
|
||
$cacheKey = 'admin_user_' . $user['id'] . '_' . $tenant_id; // 缓存键加入租户ID,避免多租户冲突
|
||
$cacheExpire = 86400 * 7; // 缓存7天,可根据需求调整
|
||
// 写入缓存(这里假设你使用thinkphp的Cache类,若用其他工具可替换)
|
||
\think\facade\Cache::set($cacheKey, $userInfo, $cacheExpire);
|
||
} catch (\Exception $e) {
|
||
error_log('用户缓存写入失败: ' . $e->getMessage());
|
||
// 缓存写入失败不影响登录,但记录日志
|
||
}
|
||
|
||
// 12. 记录登录成功日志
|
||
try {
|
||
$this->logSuccess('登录管理', '登录', [
|
||
'id' => $user['id'],
|
||
'tenant_id' => $tenant_id,
|
||
'tenant' => $tenant
|
||
], $userInfo);
|
||
} catch (\Exception $e) {
|
||
error_log('登录日志记录失败: ' . $e->getMessage());
|
||
}
|
||
|
||
// 13. 返回登录结果(包含租户信息)
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '登录成功',
|
||
'data' => [
|
||
'token' => $token,
|
||
'user' => $userInfo // 前端可直接获取租户ID和名称
|
||
]
|
||
]);
|
||
} catch (\Exception $e) {
|
||
$errorMsg = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine();
|
||
error_log('登录失败: ' . $errorMsg);
|
||
try {
|
||
$this->logFail('登录管理', '登录', $errorMsg);
|
||
} catch (\Exception $logError) {
|
||
error_log('记录登录失败日志也失败: ' . $logError->getMessage());
|
||
}
|
||
return json([
|
||
'code' => 500,
|
||
'msg' => '登录失败:' . $e->getMessage()
|
||
]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 退出登录
|
||
* @return Json
|
||
*/
|
||
public function logout(): Json
|
||
{
|
||
$authHeader = $this->request->header('Authorization', '');
|
||
$userInfo = null;
|
||
|
||
if (preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
|
||
$tokenData = $this->verifyToken($matches[1]);
|
||
if ($tokenData && isset($tokenData['user'])) {
|
||
$userInfo = (array)$tokenData['user'];
|
||
}
|
||
}
|
||
|
||
if ($userInfo && isset($userInfo['id'])) {
|
||
$this->logSuccess('登录管理', '退出登录', ['result' => 'success'], $userInfo);
|
||
} else {
|
||
\app\model\OperationLog::create([
|
||
'user_id' => 0,
|
||
'user_account' => '',
|
||
'user_name' => '未知用户',
|
||
'module' => '登录管理',
|
||
'action' => '退出登录',
|
||
'method' => 'POST',
|
||
'url' => $this->request->url(true),
|
||
'ip' => $this->request->ip(),
|
||
'user_agent' => $this->request->header('user-agent', ''),
|
||
'request_data' => null,
|
||
'response_data' => json_encode(['result' => 'success'], JSON_UNESCAPED_UNICODE),
|
||
'status' => 1,
|
||
'error_message' => '',
|
||
'execution_time' => 0.0,
|
||
]);
|
||
}
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '退出成功'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* 获取当前登录用户信息
|
||
* @return Json
|
||
*/
|
||
public function userInfo(): Json
|
||
{
|
||
$authHeader = $this->request->header('Authorization', '');
|
||
|
||
if (!preg_match('/Bearer\s+(.+)/i', $authHeader, $matches)) {
|
||
return json([
|
||
'code' => 401,
|
||
'msg' => '未登录'
|
||
]);
|
||
}
|
||
|
||
$tokenData = $this->verifyToken($matches[1]);
|
||
|
||
if (!$tokenData || !isset($tokenData['user'])) {
|
||
return json([
|
||
'code' => 401,
|
||
'msg' => 'Token无效'
|
||
]);
|
||
}
|
||
|
||
$user = (array)$tokenData['user'];
|
||
$user_id = $user['id'];
|
||
|
||
$userData = AdminUser::where('id', $user_id)
|
||
->where('delete_time', null)
|
||
->field('id, account, name, phone, qq, sex, group_id, status, create_time, update_time')
|
||
->find();
|
||
|
||
if (!$userData) {
|
||
return json([
|
||
'code' => 404,
|
||
'msg' => '用户不存在'
|
||
]);
|
||
}
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '获取成功',
|
||
'data' => $userData->toArray()
|
||
]);
|
||
}
|
||
|
||
public function getAdminUserFromToken(): array
|
||
{
|
||
return JwtService::getUserFromHeader($this->request->header('Authorization', ''));
|
||
}
|
||
|
||
|
||
|
||
public function loginInfo()
|
||
{
|
||
|
||
$loginInfo = SystemSiteSettings::select();
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '获取成功',
|
||
'data' => $loginInfo
|
||
]);
|
||
}
|
||
}
|