Merge branch 'feature/xsm' into develop

This commit is contained in:
2026-03-03 09:46:19 +08:00
4 changed files with 262 additions and 0 deletions
+199
View File
@@ -0,0 +1,199 @@
<?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
{
$payload = [
'user_id' => $user->id,
'sn' => $user->sn ?? '',
'name' => $user->name ?? '',
'iat' => time(),
'exp' => time() + config('jwt.jwt_expiration'),
];
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' => date('Y-m-d H:i:s', $expireAt),
]);
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace app\common\utils;
class SystemAuth
{
/**
* 生成签名
* @param string $userId 用户ID
* @param int $timestamp Unix时间戳
* @param string $secret 共享密钥
* @return string
*/
public static function generateSignature(string $userId, int $timestamp, string $secret): string
{
$data = $userId . $timestamp;
return hash_hmac('sha256', $data, $secret);
}
/**
* 验证签名
* @param string $userId 用户ID
* @param int $timestamp Unix时间戳
* @param string $receivedSignature 接收到的签名
* @param string $secret 共享密钥
* @return bool
*/
public static function verifySignature(string $userId, int $timestamp, string $receivedSignature, string $secret): bool
{
$expectedSignature = self::generateSignature($userId, $timestamp, $secret);
return hash_equals($expectedSignature, $receivedSignature);
}
/**
* 验证时间戳是否在有效期内
* @param int $timestamp Unix时间戳
* @param int $maxSeconds 有效期(秒)
* @return bool
*/
public static function verifyTimestamp(int $timestamp, int $maxSeconds = 600): bool
{
$now = time();
$diff = abs($now - $timestamp);
return $diff <= $maxSeconds;
}
}
+5
View File
@@ -29,6 +29,11 @@ use Webman\Route;
Route::any('/c', [app\controller\IndexController::class, 'createResource']);
Route::any('/perm', [app\controller\IndexController::class, 'perm']);
// 系统间认证接口
Route::group('/api/system', function () {
Route::any('/auth', [app\common\controller\SystemController::class, 'auth']);
});
// 飞书
Route::any('/feishu/event', [app\common\controller\NotifyController::class, 'feishuEvent']);
// 飞书回调
+12
View File
@@ -0,0 +1,12 @@
<?php
return [
// 共享密钥(双方约定同一个字符串)
'shared_secret' => env('SYSTEM_AUTH_SHARED_SECRET', 'your-shared-secret-string'),
// 签名有效期(秒),默认10分钟
'signature_expire' => 600,
// Token 有效期(秒)
'token_expire' => 86400,
];