Files
dafuweng-saiadmin6.x/server/app/api/middleware/TokenMiddleware.php

88 lines
3.5 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
declare(strict_types=1);
namespace app\api\middleware;
use app\api\cache\UserCache;
use app\api\util\ReturnCode;
use app\dice\model\player\DicePlayer;
use plugin\saiadmin\exception\ApiException;
use Tinywan\Jwt\JwtToken;
use Tinywan\Jwt\Exception\JwtTokenException;
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
/**
* 校验 token 请求头JWT
* 解码 JWT 取 username与 Redis 中当前有效 token 比对;不一致则旧 token 已失效,请重新登录
* 通过后注入 request->player_id、request->player
*/
class TokenMiddleware implements MiddlewareInterface
{
public function process(Request $request, callable $handler): Response
{
$token = $request->header('token');
if ($token === null || $token === '') {
$auth = $request->header('authorization');
if ($auth && stripos($auth, 'Bearer ') === 0) {
$token = trim(substr($auth, 7));
}
}
$token = $token !== null ? trim((string) $token) : '';
if ($token === '') {
throw new ApiException('Please provide token', ReturnCode::UNAUTHORIZED);
}
try {
$decoded = JwtToken::verify(1, $token);
} catch (JwtTokenExpiredException $e) {
throw new ApiException('Token expired, please login again', ReturnCode::TOKEN_INVALID);
} catch (JwtTokenException $e) {
throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID);
} catch (\Throwable $e) {
throw new ApiException('Token format invalid', ReturnCode::TOKEN_INVALID);
}
$extend = $decoded['extend'] ?? [];
if ((string) ($extend['plat'] ?? '') !== 'api_login') {
throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID);
}
$username = trim((string) ($extend['username'] ?? ''));
if ($username === '') {
throw new ApiException('Invalid or expired token', ReturnCode::TOKEN_INVALID);
}
$currentToken = UserCache::getSessionTokenByUsername($username);
if ($currentToken === null || $currentToken === '') {
$player = DicePlayer::where('username', $username)->find();
if (!$player) {
throw new ApiException('Please register', ReturnCode::TOKEN_INVALID);
}
throw new ApiException('Please login again', ReturnCode::TOKEN_INVALID);
}
if ($currentToken !== $token) {
throw new ApiException('Please login again (account logged in elsewhere)', ReturnCode::TOKEN_INVALID);
}
// 优先从 Redis 缓存取玩家,避免每次请求都查库
$player = null;
$cached = UserCache::getPlayerByUsername($username);
if ($cached !== null && isset($cached['id'])) {
$player = (new DicePlayer())->data($cached, true);
}
if ($player === null) {
$player = DicePlayer::where('username', $username)->find();
if (!$player) {
UserCache::deleteSessionByUsername($username);
throw new ApiException('Please login again', ReturnCode::TOKEN_INVALID);
}
UserCache::setPlayerByUsername($username, $player->hidden(['password'])->toArray());
}
$request->player_id = (int) $player->id;
$request->player = $player;
return $handler($request);
}
}