Files
fdesk/app/common/controller/SystemController.php
T
2026-03-03 16:08:53 +08:00

212 lines
6.6 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
namespace app\common\controller;
use app\common\traits\ResponseTrait;
use support\Request;
use support\Response;
use app\common\utils\SystemAuth;
use app\common\utils\Jwt;
use app\common\model\User;
use app\common\model\Systems;
use support\Redis;
class SystemController
{
use ResponseTrait;
protected $noNeedLogin = ['auth'];
protected $noNeedHeaderData = ['auth'];
/**
* 系统间认证接口
* POST /api/system/auth
*/
public function auth(Request $request): Response
{
$userId = $request->post('user_id');
$timestamp = (int)$request->post('timestamp');
$signature = $request->post('signature');
$systemCode = $request->post('system_code');
// 参数验证
if (empty($userId) || empty($timestamp) || empty($signature)) {
return $this->error('参数不完整', 400);
}
// 获取配置
$secret = config('system_auth.shared_secret');
$expire = config('system_auth.signature_expire', 600);
// 验证时间戳(10分钟有效期)
if (!SystemAuth::verifyTimestamp($timestamp, $expire)) {
return $this->error('请求已过期', 401);
}
// 验证签名
if (!SystemAuth::verifySignature($userId, $timestamp, $signature, $secret)) {
return $this->error('签名验证失败', 401);
}
// 查找用户
$user = User::find($userId);
if (!$user) {
return $this->error('用户不存在', 404);
}
// 验证系统(如果提供了 system_code)
$system = null;
if ($systemCode) {
$system = Systems::where('code', $systemCode)->first();
if (!$system) {
return $this->error('系统不存在', 404);
}
}
// 获取 corp_abbr(优先从请求参数获取,否则通过用户 corp_id 查询)
$corpAbbr = $request->post('corp_abbr');
if (!$corpAbbr && $user->corp_id) {
$corp = \app\common\model\Corp::find($user->corp_id);
$corpAbbr = $corp ? $corp->abbr : '';
}
// 构建 JWT payload
$payload = $this->buildJwtPayload($user, $system);
// 生成 access_token
$jwt = new Jwt();
$token = $jwt->generateToken($payload);
// 生成 refresh_token
$refreshToken = $this->generateRefreshToken($user->id, $systemCode ?? '');
// 获取 home_url
$homeUrl = $system ? $this->getHome($system->id, $payload) : '';
// 记录 token 到 Redis 和 MySQL
$this->recordUserToken($user->id, $token, $systemCode ?? '');
// 构建用户信息(去除敏感信息)
$userInfo = [
'id' => $user->id,
'sn' => $user->sn ?? '',
'name' => $user->name ?? '',
'avatar' => $user->avatar ?? '',
'phone' => $user->phone ?? '',
'email' => $user->email ?? '',
'sex' => $user->sex ?? '',
'position' => $user->position ?? '',
'status' => $user->status ?? 1,
'corp_id' => $user->corp_id,
];
return $this->success([
'access_token' => $token,
'refresh_token' => $refreshToken,
'expires_in' => config('jwt.jwt_expiration'),
'system_code' => $systemCode ?? '',
'corp_abbr' => $corpAbbr ?? '',
'login_type' => 'system',
'home_url' => $homeUrl,
'user_info' => $userInfo,
], '认证成功');
}
/**
* 构建 JWT payload
*/
private function buildJwtPayload(User $user, ?Systems $system): array
{
$now = time();
$exp = $now + config('jwt.jwt_expiration');
$system_ids = $user->systems ? $user->systems->pluck('id')->toArray() : [];
$role_ids = $user->roles ? $user->roles->pluck('id')->toArray() : [];
$payload = [
'sub' => 'user_' . $user->id,
'aud' => $system->code,
'iat' => $now,
'exp' => $exp,
'nbf' => $now,
'typ' => 'access',
'uid' => $user->id,
'uname' => $user->name,
'role_ids' => $role_ids,
'system_ids' => $system_ids,
// 'user_id' => $user->id,
// 'sn' => $user->sn ?? '',
// 'name' => $user->name ?? '',
];
if ($system) {
$payload['system_code'] = $system->code;
$payload['system_id'] = $system->id;
}
return $payload;
}
/**
* 生成 refresh_token
*/
private function generateRefreshToken(int $userId, string $systemCode): string
{
$refreshToken = bin2hex(random_bytes(32));
// 存储到 Redis
$key = "refresh_token:{$refreshToken}";
$expire = config('jwt.jwt_refresh_token_expiration');
Redis::setex($key, $expire, json_encode([
'user_id' => $userId,
'system_code' => $systemCode,
]));
return $refreshToken;
}
/**
* 获取用户首页地址
*/
private function getHome(int $systemId, array $payload): string
{
// 简单实现,可以根据实际需求调整
return '';
}
/**
* 记录用户token到 Redis 和 MySQL
*/
private function recordUserToken(int $userId, string $token, string $systemCode): void
{
$request = request();
$ip = $request ? $request->getRealIp() : '';
$ua = $request ? $request->header('user-agent', '') : '';
$now = time();
$expireAt = $now + config('jwt.jwt_expiration');
// Redis Set 用户token集合
Redis::sAdd("user_tokens:{$userId}:{$systemCode}", $token);
Redis::expire("user_tokens:{$userId}:{$systemCode}", config('jwt.jwt_expiration'));
// Redis Hash token元信息
$meta = [
'system_code' => $systemCode,
'login_ip' => $ip,
'user_agent' => $ua,
'login_time' => $now,
'expired_at' => $expireAt,
];
Redis::hMSet("user_token_meta:{$token}", $meta);
Redis::expire("user_token_meta:{$token}", config('jwt.jwt_expiration'));
// MySQL 持久化
\app\common\model\UserTokenLog::create([
'user_id' => $userId,
'token' => $token,
'system_code' => $systemCode,
'login_ip' => $ip,
'user_agent' => $ua,
'expired_at' => $expireAt,
]);
}
}