diff --git a/app/common/middleware/AllowCrossDomain.php b/app/common/middleware/AllowCrossDomain.php index 347eb79..bb8d8b4 100644 --- a/app/common/middleware/AllowCrossDomain.php +++ b/app/common/middleware/AllowCrossDomain.php @@ -21,6 +21,46 @@ class AllowCrossDomain implements MiddlewareInterface 'Access-Control-Allow-Headers' => '*', ]; + /** + * 根据 Origin 与配置写入 Access-Control-Allow-Origin。 + * 注意:* 与 Access-Control-Allow-Credentials:true 不能同时出现,故通配时去掉 Credentials。 + */ + private static function applyCorsOrigin(Request $request, array $header): array + { + $origin = $request->header('origin'); + if (is_array($origin)) { + $origin = $origin[0] ?? ''; + } + $origin = is_string($origin) ? trim($origin) : ''; + + $corsDomain = array_map('trim', explode(',', config('buildadmin.cors_request_domain', ''))); + $corsDomain[] = $request->host(true); + $wildcard = in_array('*', $corsDomain); + + if ($origin !== '') { + $info = parse_url($origin); + $host = ''; + if (is_array($info)) { + $host = $info['host'] ?? ''; + } + $allowed = $wildcard + || in_array($origin, $corsDomain) + || in_array($host, $corsDomain) + || ($host === 'localhost' || $host === '127.0.0.1'); + if ($allowed) { + $header['Access-Control-Allow-Origin'] = $origin; + } + return $header; + } + + if ($wildcard) { + $header['Access-Control-Allow-Origin'] = '*'; + unset($header['Access-Control-Allow-Credentials']); + } + + return $header; + } + /** * 返回 CORS 预检(OPTIONS)响应,供路由直接调用(Webman 未匹配路由时不走中间件) */ @@ -32,24 +72,7 @@ class AllowCrossDomain implements MiddlewareInterface 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, batoken, ba-user-token, think-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; - } - } + $header = self::applyCorsOrigin($request, $header); return response('', 204, $header); } @@ -60,29 +83,7 @@ class AllowCrossDomain implements MiddlewareInterface 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; - } - } + $header = self::applyCorsOrigin($request, $this->header); if ($request->method() === 'OPTIONS') { return response('', 204, $header); diff --git a/app/process/Http.php b/app/process/Http.php index c2f1f0d..2228154 100644 --- a/app/process/Http.php +++ b/app/process/Http.php @@ -3,12 +3,12 @@ namespace app\process; use Webman\App; -use Webman\Http\Response; class Http extends App { /** * 在父类处理前拦截 OPTIONS 预检,直接返回 CORS 头(避免预检未命中路由时无 CORS) + * 与 AllowCrossDomain::optionsResponse 一致,避免 * + Allow-Credentials 组合被浏览器拒绝 */ public function onMessage($connection, $request): void { @@ -18,19 +18,8 @@ class Http extends App $path = is_string($path) ? trim($path, '/') : ''; $isApiOrAdmin = $path !== '' && (str_starts_with($path, 'api') || str_starts_with($path, 'admin')); if ($isApiOrAdmin) { - $origin = $request->header('origin'); - $origin = is_array($origin) ? ($origin[0] ?? '') : (is_string($origin) ? trim($origin) : ''); - if ($origin === '') { - $origin = '*'; - } - $headers = [ - 'Access-Control-Allow-Origin' => $origin, - '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', - ]; - $connection->send(new Response(204, $headers, '')); + $response = \app\common\middleware\AllowCrossDomain::optionsResponse($request); + $connection->send($response); return; } }