昨天先去了解了下同源策略,然后又去看了一些和 CORS 相关的文章,几乎所有的文章都告诉我只要在响应头中加入几个 Access-Control-* 头部就可以了。比如像这样:
header('Access-Control-Allow-Origin:http://www.startphp.cn');
header('Access-Control-Allow-Methods:POST');
header('Access-Control-Allow-Headers:x-requested-with, content-type');
这段代码是我从别的地方复制过来的,不同文章的具体代码不一样,但都是差不多的,只不过多几个头部或者某个头部的值不一样罢了,没什么本质区别。
服务端这样做确实可以通知客户端,让其拒绝服务端返回的响应,但是服务端关于 CORS 的全部工作就这些么?我觉得不是!至少还应该加一些处理,起码应该在适当的时候结束程序的执行,如果服务端都准备告诉客户端拒绝自己的响应了,那发完响应头部,就没必要再执行后续的业务逻辑了,不是么?反过来说,所有的逻辑都执行完了,然后你再加几个头部去告诉客户端把自己拒绝掉,这不是多此一举么?
我知道客户端只要不发送 Origin 头部,就可以绕过服务端的 CORS 处理;我也知道要想提升安全性,必须得做鉴权才行,但就是觉得服务端的 CORS 处理这块还是应该稍微完善一点,不应该只是加几个头部就草草的结束了才对。如果你觉得我的这种想法是有问题且多余的,欢迎留言指正!
最后我列出我的一点代码,代码并没有做过测试,也更没在开发环境和生产环境中使用过,只是想说明一下我觉得完善的 CORS 处理大概应该是什么样子。具体的逻辑主要参考的是阮一峰的这篇文章:《跨域资源共享 CORS 详解》 。有兴趣的伙伴可以去看看。
<?php
// CORS 配置
$config = [
'allow_origin' => ["http://b.example.com", "http://c.example.com"],
'allow_method' => ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
'allow_headers' => ['X-Custom-Header', 'Accept'],
'expose_headers' => ['Content-Range'],
'allow_credentials' => true,
'max_age' => 86400,
];
// CORS 预检请求
if (isCorsPreflightRequest()) {
// 检查预检请求
if (checkCorsPreflightRequest($config)) {
// 通过
addCorsPreflightRequestHeaders($config);
} else {
// 未通过,什么也不做
}
// 退出,不进入业务逻辑部分
exit;
}
// CORS 简单请求
if (isCorsSimpleRequest()) {
// 检查简单请求
if (checkCorsSimpleRequest($config)) {
// 通过
addCorsSimpleRequestHeaders($config);
} else {
// 没通过,退出,不进入业务逻辑部分
exit;
}
}
function isCorsPreflightRequest(): bool
{
return isset($_SERVER['HTTP_ORIGIN'], $_SERVER['REQUEST_METHOD']) &&
'OPTIONS' === strtoupper($_SERVER['REQUEST_METHOD']);
}
function checkCorsPreflightRequest(array $config): bool
{
return checkCorsOrigin($config) && checkCorsRequestMethod($config) && checkCorsRequestHeaders($config);
}
function checkCorsOrigin(array $config): bool
{
return '*' === $config['allow_origin'] || in_array($_SERVER['HTTP_ORIGIN'], $config['allow_origin'], true);
}
function checkCorsRequestMethod(array $config): bool
{
if (
isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) &&
!in_array(strtoupper($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']), $config['allow_method'], true)
) {
return false;
}
return true;
}
function checkCorsRequestHeaders(array $config): bool
{
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
$requestHeaders = explode(',', $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
$allowHeaders = array_map(fn($header) => strtoupper($header), $config['allow_headers']);
foreach ($requestHeaders as $requestHeader) {
$requestHeader = strtoupper(trim($requestHeader));
if (!in_array($requestHeader, $allowHeaders, true)) {
return false;
}
}
}
return true;
}
function addCorsPreflightRequestHeaders(array $config): void
{
if ('*' === $config['allow_origin']) {
header('Access-Control-Allow-Origin: *');
} else {
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
}
header('Access-Control-Allow-Methods: ' . implode(',', $config['allow_method']));
header('Access-Control-Allow-Headers: ' . implode(',', $config['allow_headers']));
header('Access-Control-Allow-Credentials: ' . ($config['allow_credentials'] ? 'true' : 'false'));
header('Access-Control-Max-Age: ' . $config['max_age']);
}
function isCorsSimpleRequest(): bool
{
return isset($_SERVER['HTTP_ORIGIN']);
}
function checkCorsSimpleRequest(array $config): bool
{
return checkCorsOrigin($config);
}
function addCorsSimpleRequestHeaders(array $config, array $exposeHeaders = []): void
{
if ('*' === $config['allow_origin']) {
header('Access-Control-Allow-Origin: *');
} else {
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
}
if (empty($exposeHeaders)) {
header('Access-Control-Allow-Headers: ' . implode(',', $config['expose_headers']));
} else {
header('Access-Control-Allow-Headers: ' . implode(',', $exposeHeaders));
}
header('Access-Control-Allow-Credentials: ' . ($config['allow_credentials'] ? 'true' : 'false'));
}
// 假设下方是业务逻辑部分的代码
// 省略...
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.