hyperf-view/api/Middleware/VerifyInterfaceMiddleware.php

196 lines
6.5 KiB
PHP

<?php
/**
* MineAdmin is committed to providing solutions for quickly building web applications
* Please view the LICENSE file that was distributed with this source code,
* For the full copyright and license information.
* Thank you very much for using MineAdmin.
*
* @Author X.Mo<root@imoi.cn>
* @Link https://gitee.com/xmo/MineAdmin
*/
declare(strict_types=1);
namespace Api\Middleware;
use App\System\Service\SystemAppService;
use Builder\Event\ApiAfter;
use Builder\Event\ApiBefore;
use App\System\Model\SystemApi;
use App\System\Service\SystemApiService;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Context\Context;
use Builder\Exception\NormalStatusException;
use Builder\Helper\BaseCode;
use Builder\BaseRequest;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class VerifyInterfaceMiddleware implements MiddlewareInterface
{
/**
* 事件调度器
* @var EventDispatcherInterface
*/
#[Inject]
protected EventDispatcherInterface $evDispatcher;
/**
* 验证检查接口
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler):ResponseInterface
{
$this->crossSetting($request);
return $this->run($request, $handler);
}
/**
* 跨域设置
* @param $request
*/
protected function crossSetting($request): void
{
$crossData = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST,GET,PUT,DELETE,OPTIONS',
'Access-Control-Allow-Headers' => 'Version, Access-Token, User-Token, Api-Auth, User-Agent, Keep-Alive, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With',
'Access-Control-Allow-Credentials' => 'true'
];
foreach ($crossData as $name => $value) {
$request->withHeader($name, $value);
}
}
/**
* 访问接口鉴权处理
* @param ServerRequestInterface $request
* @return int
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
protected function auth(ServerRequestInterface $request): int
{
try {
/* @var $service SystemAppService */
$service = container()->get(SystemAppService::class);
$queryParams = $request->getQueryParams();
switch ($this->_getApiData()['auth_mode']) {
case SystemApi::AUTH_MODE_EASY:
if (empty($queryParams['app_id'])) {
return BaseCode::API_APP_ID_MISSING;
}
if (empty($queryParams['identity'])) {
return BaseCode::API_IDENTITY_MISSING;
}
return $service->verifyEasyMode($queryParams['app_id'], $queryParams['identity']);
case SystemApi::AUTH_MODE_NORMAL:
if (empty($queryParams['access_token'])) {
return BaseCode::API_ACCESS_TOKEN_MISSING;
}
return $service->verifyNormalMode($queryParams['access_token']);
default:
throw new \RuntimeException();
}
} catch (\Throwable $e) {
throw new NormalStatusException(t('uiview.api_auth_exception'), BaseCode::API_AUTH_EXCEPTION);
}
}
/**
* API常规检查
* @throws NotFoundExceptionInterface
* @throws ContainerExceptionInterface
*/
protected function apiModelCheck($request): ServerRequestInterface
{
$service = container()->get(SystemApiService::class);
$apiModel = $service->mapper->one(function($query) {
$request = container()->get(BaseRequest::class);
$query->where('access_name', $request->route('method'));
});
// 检查接口是否存在
if (! $apiModel) {
throw new NormalStatusException(t('uiview.not_found'), BaseCode::NOT_FOUND);
}
// 检查接口是否停用
if ($apiModel['status'] == SystemApi::DISABLE) {
throw new NormalStatusException(t('uiview.api_stop'), BaseCode::RESOURCE_STOP);
}
// 检查接口请求方法
if ($apiModel['request_mode'] !== SystemApi::METHOD_ALL && $request->getMethod()[0] !== $apiModel['request_mode']) {
throw new NormalStatusException(
t('uiview.not_allow_method', ['method' => $request->getMethod()]),
BaseCode::METHOD_NOT_ALLOW
);
}
$this->_setApiData($apiModel->toArray());
// 合并入参
return $request->withParsedBody(array_merge(
$request->getParsedBody(), ['apiData' => $apiModel->toArray()]
));
}
/**
* 运行
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
protected function run(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->evDispatcher->dispatch(new ApiBefore());
$request = $this->apiModelCheck($request);
if (($code = $this->auth($request)) !== BaseCode::API_VERIFY_PASS) {
throw new NormalStatusException(t('uiview.api_auth_fail'), $code);
}
$result = $handler->handle($request);
$event = new ApiAfter($this->_getApiData(), $result);
$this->evDispatcher->dispatch($event);
return $event->getResult();
}
/**
* 设置协程上下文
* @param array $data
*/
private function _setApiData(array $data)
{
Context::set('apiData', $data);
}
/**
* 获取协程上下文
* @return array
*/
private function _getApiData(): array
{
return Context::get('apiData', []);
}
}