76 lines
2.5 KiB
PHP
76 lines
2.5 KiB
PHP
<?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;
|
||
|
||
/**
|
||
* API 鉴权中间件
|
||
* 校验请求头 auth-token(或 Authorization: Bearer xxx),白名单路径不校验
|
||
*/
|
||
class CheckApiAuthMiddleware implements MiddlewareInterface
|
||
{
|
||
/** 不需要 auth-token 的路径(仅获取 token 的接口) */
|
||
private const WHITELIST = [
|
||
'api/authToken',
|
||
];
|
||
|
||
public function process(Request $request, callable $handler): Response
|
||
{
|
||
$path = trim($request->path(), '/');
|
||
if ($this->isWhitelist($path)) {
|
||
return $handler($request);
|
||
}
|
||
|
||
$token = $request->header('auth-token');
|
||
if (empty($token)) {
|
||
$auth = $request->header('authorization');
|
||
if ($auth && stripos($auth, 'Bearer ') === 0) {
|
||
$token = trim(substr($auth, 7));
|
||
}
|
||
}
|
||
if (empty($token)) {
|
||
throw new ApiException('请携带 auth-token', ReturnCode::MISSING_TOKEN);
|
||
}
|
||
|
||
try {
|
||
// ACCESS_TOKEN = 1(JwtToken 内部私有常量)
|
||
$decoded = JwtToken::verify(1, $token);
|
||
$extend = $decoded['extend'] ?? [];
|
||
if (($extend['plat'] ?? '') !== 'api') {
|
||
throw new ApiException('auth-token 无效', ReturnCode::TOKEN_TIMEOUT);
|
||
}
|
||
} 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);
|
||
}
|
||
|
||
return $handler($request);
|
||
}
|
||
|
||
private function isWhitelist(string $path): bool
|
||
{
|
||
foreach (self::WHITELIST as $prefix) {
|
||
if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
}
|