212 lines
6.6 KiB
PHP
212 lines
6.6 KiB
PHP
<?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,
|
||
]);
|
||
}
|
||
}
|