swoole方案 跨域资源共享 (CORS) 统一处理中心

swoole方案 跨域资源共享 (CORS) 统一处理中心 大白话解释---CORS是什么问题 浏览器有个规则localhost:3000的页面里的JS不能直接请求 api.yourdomain.com会被浏览器拦截报跨域错误。 这不是服务器拦的是浏览器自己拦的保护用户不被恶意网站偷数据。---怎么解决 服务器在响应头里加一句话 Access-Control-Allow-Origin:http://localhost:3000浏览器看到这个就放行了。---OPTIONS预检是什么 浏览器发真实请求前先发一个OPTIONS请求问服务器我能来吗我要用POST方法带Authorization头。 服务器回答可以浏览器才发真实请求。 这个网关直接在门口回答OPTIONS不转发给后端后端完全不知道有这回事省了一次转发。---白名单的意义 不能写Access-Control-Allow-Origin:*因为-*不能和Allow-Credentials:true同时用带Cookie的请求必须指定具体域名-任何域名都能访问你的接口不安全 所以维护一个白名单只有列表里的域名才加CORS头其他的403拒绝。---Vary:Origin 是干嘛的 同一个URLlocalhost:3000来的响应头和localhost:5173来的不一样。 加 Vary:Origin 告诉CDN/代理这个响应不能无脑缓存要按Origin分开缓存不然A的响应头会被返回给B。---跑起来 php8.4/mnt/d/cors/cors.php 不需要任何依赖直接跑。?php/** * CORS 统一处理网关 * * 大白话 * 浏览器有个安全规则A网站的JS不能直接请求B网站的接口 * 除非B网站在响应头里说我允许A来访问 * 这个网关统一加这些头后端服务不用每个都自己处理 * * 流程 * 浏览器发请求 → 这里加CORS头 → 转发给后端 → 返回 * * 运行 * php8.4 cors.php */useSwoole\Http\Server;useSwoole\Http\Request;useSwoole\Http\Response;useSwoole\Coroutine\Http\Client;// 允许跨域的域名白名单// key域名, value该域名允许的HTTP方法constCORS_RULES[https://app.yourdomain.com[GET,POST,PUT,DELETE],https://admin.yourdomain.com[GET,POST],http://localhost:3000[GET,POST,PUT,DELETE,PATCH],http://localhost:5173[GET,POST,PUT,DELETE,PATCH],];// 允许携带的请求头constALLOWED_HEADERSContent-Type, Authorization, X-Requested-With, X-User-Id;// 预检结果缓存时间秒// 浏览器在这段时间内不会重复发 OPTIONS 预检直接发真实请求constPREFLIGHT_CACHE7200;// 后端服务constBACKEND_HOST127.0.0.1;constBACKEND_PORT8080;$servernewServer(0.0.0.0,9700);$server-set([worker_numswoole_cpu_num(),enable_coroutinetrue,]);$server-on(request,function(Request$req,Response$res){$origin$req-header[origin]??;$method$req-server[request_method];// ── 判断来源是否在白名单 ──────────────────────────────────$allowedMethodsCORS_RULES[$origin]??null;if($origin!$allowedMethods!null){// 在白名单里加 CORS 头$res-header(Access-Control-Allow-Origin,$origin);$res-header(Access-Control-Allow-Methods,implode(, ,$allowedMethods));$res-header(Access-Control-Allow-Headers,ALLOWED_HEADERS);$res-header(Access-Control-Allow-Credentials,true);// 允许携带 Cookie$res-header(Access-Control-Max-Age,(string)PREFLIGHT_CACHE);$res-header(Vary,Origin);// 告诉CDN这个响应因Origin不同而不同别缓存错}elseif($origin!){// 有 Origin 但不在白名单直接拒绝$res-status(403);$res-end(json_encode([errororigin not allowed]));return;}// 没有 Origin 说明不是跨域请求比如服务端直接调正常放行// ── OPTIONS 预检请求浏览器在发真实请求前先问我能来吗 ──// 直接回复不用转发给后端后端根本不需要知道有这回事if($methodOPTIONS){$res-status(204);// 204 No Content我知道了没内容$res-end();return;}// ── 转发真实请求给后端 ────────────────────────────────────$cnewClient(BACKEND_HOST,BACKEND_PORT);$c-set([timeout10]);// 透传所有请求头但删掉 Origin后端不需要处理跨域了$headers$req-header??[];unset($headers[origin]);$c-setHeaders($headers);$c-setMethod($method);$body$req-rawContent();if($body!$body!false){$c-setData($body);}$qs$req-server[query_string]??;$uri$req-server[request_uri].($qs!??$qs:);$c-execute($uri);if($c-statusCode0){$res-status(502);$res-end(json_encode([errorbackend unavailable]));$c-close();return;}// 透传后端响应头但跳过后端自己加的 CORS 头避免重复static$skipHeaders[access-control-allow-origin,access-control-allow-methods,access-control-allow-headers,access-control-allow-credentials];$res-status($c-statusCode);foreach($c-headers??[]as$k$v){if(!in_array(strtolower($k),$skipHeaders,true)){$res-header($k,$v);}}$res-end($c-body);$c-close();});$server-on(workerStart,function($s,$wid){if($wid0){echoCORS Gateway → http://0.0.0.0:9700\n;echo白名单域名: .implode(, ,array_keys(CORS_RULES)).\n;}});$server-start();