PHP 跨域的时候一个接口是 OK 的另一个不行?

axios.post(`https://aaa.cn/xml/scp-xml/php/api/index.php?class=survey&method=info`, {
    id: "SCWJ202412-000003"
}, {withCredentials: true})

axios.post(`https://aaa.cn/xml/scp-xml/php/api/index.php?class=readerStu&method=login`, {
    "account": "0888888",
    "password": "qwer"
}, {withCredentials: true})

image.png
image.png

<?php
# 获取请求的 Origin
$Origin = $_SERVER['HTTP_ORIGIN'] ?? "";
# 允许的 Origins 列表
$Allowed = [
    'http://localhost:51730',
    'http://localhost:5173',
    'https://scp-erp.aidingyi.cn'
];
# 检查请求的 Origin 是否在允许列表中
header("Access-Control-Allow-Origin: " . ( in_array( $Origin, $Allowed ) ? $Origin : "*" ) );
# 设置其他 CORS 头部
header("Access-Control-Allow-Credentials: true");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE');
header('Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type');

请问这是为什么 这也太离谱了

阅读 1.1k
avatarAI BotBETA
### 可能的原因及解决方案

1. **请求头中的 Origin 不匹配**:
   - 检查第二个请求的 Origin 是否确实在 PHP 后端配置的 `$Allowed` 列表中。可以通过浏览器的开发者工具(Network 选项卡)查看实际请求的 Origin。
   - 如果 Origin 不在列表中,需要将其添加到 `$Allowed` 数组中。

2. **凭证(Credentials)问题**:
   - 由于两个请求都设置了 `{withCredentials: true}`,这意味着浏览器会发送 Cookies 和 HTTP 认证信息。
   - 确保服务器对于这两个接口都正确处理了凭证。特别是检查服务器端的 Cookie 设置(如 `SameSite` 属性)是否影响了凭证的发送。

3. **服务器端配置差异**:
   - 检查服务器端对于这两个接口的处理逻辑是否有差异,特别是与 CORS 相关的中间件或配置。
   - 确认两个接口是否部署在不同的服务器或子域名上,这可能会影响 CORS 策略。

4. **浏览器缓存或预检请求(Preflight Request)问题**:
   - 浏览器在发送跨域 POST 请求之前,通常会发送一个 OPTIONS 预检请求来检查 CORS 策略。
   - 检查 OPTIONS 请求的响应是否正确,并且服务器是否对 OPTIONS 请求也设置了正确的 CORS 头部。
   - 清除浏览器缓存或尝试在无痕/隐私模式下访问,看是否能解决问题。

5. **网络问题或服务器配置错误**:
   - 检查服务器日志,看是否有关于这两个请求的特定错误信息。
   - 确认服务器没有针对特定路径或方法的防火墙规则或安全组设置,这些设置可能阻止了请求的发送或接收。

6. **代码或配置错误**:
   - 重新检查 PHP 代码中的 CORS 配置部分,确保没有语法错误或逻辑错误。
   - 确认后端 API 的路由和处理逻辑是否正确无误。

### 结论

这个问题很可能是由于请求头中的 Origin 不在允许的列表中、服务器端对凭证的处理不当、或者服务器端配置差异导致的。建议从上述几个方面逐一排查,以确定问题的具体原因并采取相应的解决措施。
3 个回答

1.通配符 Origin: 当 Access-Control-Allow-Credentials 设置为 true 时,使用 "" 作为 Access-Control-Allow-Origin 的回退值可能会导致问题。如果 Access-Control-Allow-Origin 是 "" 且 Access-Control-Allow-Credentials 是 true,浏览器将阻止请求。
2.预检请求: 确保服务器正确处理 OPTIONS 请求,因为浏览器会使用这些请求来在发送实际请求之前检查 CORS 策略。

我把你的PHP代码改了一下

<?php
# 获取请求的 Origin
$Origin = $_SERVER['HTTP_ORIGIN'] ?? "";
# 允许的 Origins 列表
$Allowed = [
    'http://localhost:51730',
    'http://localhost:5173',
    'https://scp-erp.aidingyi.cn'
];
# 检查请求的 Origin 是否在允许列表中
if (in_array($Origin, $Allowed)) {
    header("Access-Control-Allow-Origin: $Origin");
    header("Access-Control-Allow-Credentials: true");
    header('Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE');
    header('Access-Control-Allow-Headers: DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type');
    
    # 处理预检请求
    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        exit(0);
    }
} else {
    header("HTTP/1.1 403 Forbidden");
    echo "CORS 策略不允许从此来源访问。";
    exit(0);
}
?>

补充

强制触发预检请求,可以通过以下三种方法:

1. 添加自定义头部

添加自定义头部可以触发预检请求,因为自定义头部使请求变得不那么“简单”。

axios.post('https://aaa.cn/xml/scp-xml/php/api/index.php?class=survey&method=info', {
    id: "SCWJ202412-000003"
}, {
    withCredentials: true,
    headers: {
        'X-CustomHeader': 'value'
    }
});

2. 使用非简单请求方法

使用非简单请求方法(例如 PUTDELETE)可以触发预检请求。

axios.put('https://aaa.cn/xml/scp-xml/php/api/index.php?class=survey&method=info', {
    id: "SCWJ202412-000003"
}, { 
    withCredentials: true 
});

3. 更改内容类型

更改内容类型为非默认值(例如 application/json)也可以触发预检请求。

axios.post('https://aaa.cn/xml/scp-xml/php/api/index.php?class=survey&method=info', {
    id: "SCWJ202412-000003"
}, {
    withCredentials: true,
    headers: {
        'Content-Type': 'application/json'
    }
});

通过这些方法,可以强制触发预检请求,以确保服务器能够正确处理跨域请求。

因为你的跨域头是通过 php header 函数发送的,可是你的一个请求代码有问题,所以这个请求头没法成功发送给浏览器,所以浏览器就提示跨域了。
调试问题多使用排除法,使用postman等调用接口,如果没有问题。但是浏览器调用提示跨域,那问题就可以定位为跨域问题,尝试通过不同方式解决。如果通过postman调用发现有一个接口报错,那就看看是不是代码有问题。
先找找自身的问题,而不是一上来就把锅甩给xxx

当我们在设置跨域请求时,Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin 这两个响应头之间的关系非常微妙,且容易混淆。

具体来说:

Access-Control-Allow-Credentials: true: 这个头字段表示允许浏览器发送带有凭证(如 Cookie、Authorization header)的跨域请求。换句话说,它打开了一扇门,允许浏览器发送更“私密”的请求。
Access-Control-Allow-Origin: 这个头字段指定了哪些源可以访问资源。
Access-Control-Allow-Credentials: true 时,Access-Control-Allow-Origin 不能设置为 "*" 的原因:

安全性考虑: "*" 表示允许所有源访问,这在允许携带凭证的情况下是非常不安全的。想象一下,如果任何网站都能发送带有 Cookie 的请求到你的服务器,那么你的用户数据将面临极大的风险。
浏览器限制: 为了安全考虑,浏览器在 Access-Control-Allow-Credentials: true 设置为 true 时,会强制要求 Access-Control-Allow-Origin 不能为 "*",而必须是一个具体的域名或域名列表。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏