Files
webman-buildadmin-mall/app/common/middleware/AllowCrossDomain.php
2026-03-30 12:45:48 +08:00

119 lines
4.2 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\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' => '*',
];
/**
* 读取预检请求中的 Access-Control-Request-Headers经 CDN/反代时 Request 可能取不到,补读 $_SERVER
*/
private static function accessControlRequestHeaders(Request $request): string
{
$reqHeaders = $request->header('access-control-request-headers', '');
if (is_array($reqHeaders)) {
$reqHeaders = $reqHeaders[0] ?? '';
}
if (is_string($reqHeaders) && trim($reqHeaders) !== '') {
return trim($reqHeaders);
}
$fromServer = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'] ?? '';
if (is_string($fromServer) && trim($fromServer) !== '') {
return trim($fromServer);
}
return '';
}
/**
* 返回 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, server',
];
$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 = self::accessControlRequestHeaders($request);
if ($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);
}
}