103 lines
3.6 KiB
PHP
103 lines
3.6 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace app\common\middleware;
|
||
|
||
use Webman\MiddlewareInterface;
|
||
use Webman\Http\Request;
|
||
use Webman\Http\Response;
|
||
|
||
/**
|
||
* 跨域请求支持(Webman 迁移版)
|
||
* 安全起见,只支持配置中的域名
|
||
*/
|
||
class AllowCrossDomain implements MiddlewareInterface
|
||
{
|
||
protected array $header = [
|
||
'Access-Control-Allow-Credentials' => 'true',
|
||
'Access-Control-Max-Age' => '1800',
|
||
'Access-Control-Allow-Methods' => '*',
|
||
'Access-Control-Allow-Headers' => '*',
|
||
];
|
||
|
||
/**
|
||
* 返回 CORS 预检(OPTIONS)响应,供路由直接调用(Webman 未匹配路由时不走中间件)
|
||
*/
|
||
public static function optionsResponse(Request $request): Response
|
||
{
|
||
$header = [
|
||
'Access-Control-Allow-Credentials' => 'true',
|
||
'Access-Control-Max-Age' => '1800',
|
||
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
||
'Access-Control-Allow-Headers' => 'Content-Type, Authorization, batoken, ba-user-token, think-lang, lang',
|
||
];
|
||
$origin = $request->header('origin');
|
||
if (is_array($origin)) {
|
||
$origin = $origin[0] ?? '';
|
||
}
|
||
$origin = is_string($origin) ? trim($origin) : '';
|
||
if ($origin !== '') {
|
||
$info = parse_url($origin);
|
||
$host = $info['host'] ?? '';
|
||
$corsDomain = array_map('trim', explode(',', config('buildadmin.cors_request_domain', '')));
|
||
$corsDomain[] = $request->host(true);
|
||
$allowed = in_array('*', $corsDomain)
|
||
|| in_array($origin, $corsDomain)
|
||
|| in_array($host, $corsDomain)
|
||
|| ($host === 'localhost' || $host === '127.0.0.1');
|
||
if ($allowed) {
|
||
$header['Access-Control-Allow-Origin'] = $origin;
|
||
// 回显浏览器在预检中声明的请求头,避免白名单遗漏导致 CORS 失败
|
||
$reqHeaders = $request->header('access-control-request-headers', '');
|
||
if (is_array($reqHeaders)) {
|
||
$reqHeaders = $reqHeaders[0] ?? '';
|
||
}
|
||
if (is_string($reqHeaders) && trim($reqHeaders) !== '') {
|
||
$header['Access-Control-Allow-Headers'] = $reqHeaders;
|
||
}
|
||
}
|
||
}
|
||
return response('', 204, $header);
|
||
}
|
||
|
||
public function process(Request $request, callable $handler): Response
|
||
{
|
||
$path = trim($request->path(), '/');
|
||
if (!str_starts_with($path, 'api/') && !str_starts_with($path, 'admin/')) {
|
||
return $handler($request);
|
||
}
|
||
|
||
$header = $this->header;
|
||
|
||
$origin = $request->header('origin');
|
||
if (is_array($origin)) {
|
||
$origin = $origin[0] ?? '';
|
||
}
|
||
$origin = is_string($origin) ? trim($origin) : '';
|
||
|
||
if ($origin !== '') {
|
||
$info = parse_url($origin);
|
||
$host = $info['host'] ?? '';
|
||
$corsDomain = array_map('trim', explode(',', config('buildadmin.cors_request_domain', '')));
|
||
$corsDomain[] = $request->host(true);
|
||
|
||
$allowed = in_array('*', $corsDomain)
|
||
|| in_array($origin, $corsDomain)
|
||
|| in_array($host, $corsDomain)
|
||
|| ($host === 'localhost' || $host === '127.0.0.1');
|
||
|
||
if ($allowed) {
|
||
$header['Access-Control-Allow-Origin'] = $origin;
|
||
}
|
||
}
|
||
|
||
if ($request->method() === 'OPTIONS') {
|
||
return response('', 204, $header);
|
||
}
|
||
|
||
$response = $handler($request);
|
||
return $response->withHeaders($header);
|
||
}
|
||
}
|