添加authToken和userToken

This commit is contained in:
2026-03-05 12:17:20 +08:00
parent a10afa5add
commit 13d8adbfe0
11 changed files with 290 additions and 210 deletions

View File

@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace app\api\middleware;
use support\Log;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;
use Tinywan\Jwt\JwtToken;
use Tinywan\Jwt\Exception\JwtTokenException;
use Tinywan\Jwt\Exception\JwtTokenExpiredException;
use app\api\util\ReturnCode;
use plugin\saiadmin\exception\ApiException;
/**
* 仅校验 auth-token 请求头
* 白名单路径(如 /api/authToken不校验其它接口必须携带有效 auth-token 或 Authorization: Bearer <token>,且必须通过 JWT 签名与 plat=api 校验
*/
class CheckAuthTokenMiddleware implements MiddlewareInterface
{
/** 不需要 auth-token 的路径 */
private const WHITELIST = [
'api/authToken',
];
/** JWT 至少为 xxx.yyy.zzz 三段 */
private const JWT_PARTS_MIN = 3;
public function process(Request $request, callable $handler): Response
{
$path = trim((string) $request->path(), '/');
if ($this->isWhitelist($path)) {
return $handler($request);
}
$token = $this->getAuthTokenFromRequest($request);
if ($token === '') {
throw new ApiException('请携带 auth-token', ReturnCode::MISSING_TOKEN);
}
if (!$this->looksLikeJwt($token)) {
throw new ApiException('auth-token 格式无效', ReturnCode::TOKEN_TIMEOUT);
}
$decoded = $this->verifyAuthToken($token);
$extend = $decoded['extend'] ?? [];
if (($extend['plat'] ?? '') !== 'api') {
throw new ApiException('auth-token 无效(非 API 凭证)', ReturnCode::TOKEN_TIMEOUT);
}
return $handler($request);
}
private function getAuthTokenFromRequest(Request $request): string
{
$token = $request->header('auth-token');
if ($token !== null && $token !== '') {
return trim((string) $token);
}
$auth = $request->header('authorization');
if ($auth && stripos($auth, 'Bearer ') === 0) {
return trim(substr($auth, 7));
}
return '';
}
private function looksLikeJwt(string $token): bool
{
$parts = explode('.', $token);
return count($parts) >= self::JWT_PARTS_MIN;
}
/**
* 校验 auth-token 有效性签名、过期、iss 等),无效或过期必抛 ApiException
*/
private function verifyAuthToken(string $token): array
{
try {
return JwtToken::verify(1, $token);
} catch (JwtTokenExpiredException $e) {
Log::error('auth-token 已过期, 报错信息' . $e);
throw new ApiException('auth-token 已过期', ReturnCode::TOKEN_TIMEOUT);
} catch (JwtTokenException $e) {
Log::error('auth-token 无效, 报错信息' . $e);
throw new ApiException($e->getMessage() ?: 'auth-token 无效', ReturnCode::TOKEN_TIMEOUT);
} catch (\Throwable $e) {
Log::error('auth-token 校验失败, 报错信息' . $e);
throw new ApiException('auth-token 校验失败', ReturnCode::TOKEN_TIMEOUT);
}
}
private function isWhitelist(string $path): bool
{
foreach (self::WHITELIST as $prefix) {
if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
return true;
}
}
return false;
}
}