PHP 跨域问题解决方案
跨域问题(CORS - Cross-Origin Resource Sharing)是浏览器的安全机制,当网页尝试从不同源(协议、域名、端口)请求资源时会被阻止。以下是 PHP 中解决跨域问题的几种方法:
🔧 解决方案
1. 设置 CORS 响应头
在 PHP 脚本开始处添加必要的 CORS 头信息:
<?php
// 允许所有域名访问
header("Access-Control-Allow-Origin: *");
// 允许特定域名访问
header("Access-Control-Allow-Origin: https://example.com");
// 允许特定 HTTP 方法
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
// 允许特定请求头
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
// 允许携带凭证(cookies)
header("Access-Control-Allow-Credentials: true");
// 设置预检请求缓存时间
header("Access-Control-Max-Age: 86400"); // 24小时
?>
2. 处理预检请求(OPTIONS)
浏览器在发送复杂请求前会先发送 OPTIONS 预检请求:
<?php
// 检查请求方法
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
// 设置 CORS 头
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Max-Age: 86400");
// 直接返回 200 状态码,不执行后续代码
http_response_code(200);
exit();
}
// 正常处理请求
header("Access-Control-Allow-Origin: *");
// 其他业务逻辑...
?>
3. 创建 CORS 中间件函数
封装跨域处理逻辑:
<?php
function setCORSHeaders($allowedOrigins = [], $allowCredentials = false) {
// 获取请求来源
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
// 如果没有指定允许的域名,则允许所有域名
if (empty($allowedOrigins)) {
header("Access-Control-Allow-Origin: *");
} else {
// 检查请求来源是否在允许列表中
if (in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: " . $origin);
}
}
// 设置其他 CORS 头
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
if ($allowCredentials) {
header("Access-Control-Allow-Credentials: true");
}
header("Access-Control-Max-Age: 86400");
}
// 使用示例
setCORSHeaders(['https://example.com', 'https://app.example.com'], true);
?>
4. 针对 API 接口的完整解决方案
<?php
class CORSHandler {
private $allowedOrigins;
private $allowCredentials;
public function __construct($allowedOrigins = [], $allowCredentials = false) {
$this->allowedOrigins = $allowedOrigins;
$this->allowCredentials = $allowCredentials;
}
public function handle() {
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
$this->setHeaders();
http_response_code(200);
exit();
}
// 设置常规请求头
$this->setHeaders();
}
private function setHeaders() {
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (empty($this->allowedOrigins)) {
header("Access-Control-Allow-Origin: *");
} else {
if (in_array($origin, $this->allowedOrigins)) {
header("Access-Control-Allow-Origin: " . $origin);
}
}
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
if ($this->allowCredentials) {
header("Access-Control-Allow-Credentials: true");
}
header("Access-Control-Max-Age: 86400");
}
}
// 使用示例
$cors = new CORSHandler(['https://example.com'], true);
$cors->handle();
// 你的 API 逻辑
echo json_encode(['status' => 'success']);
?>
5. 在框架中的处理方式
Laravel 中的 CORS 处理:
// 在中间件中处理
// app/Http/Middleware/CorsMiddleware.php
public function handle($request, Closure $next) {
$response = $next($request);
$response->headers->set('Access-Control-Allow-Origin', '*');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return $response;
}
🛡️ 安全注意事项
- 避免使用通配符:生产环境中不要使用
Access-Control-Allow-Origin: *
,应该指定具体的域名 - 凭证安全:如果需要携带凭证(cookies),不要使用通配符,必须指定具体域名
- 方法限制:只允许必要的 HTTP 方法
- 请求头限制:只允许必要的自定义请求头
📝 最佳实践
- 在应用入口处统一处理 CORS
- 根据环境配置不同的 CORS 策略
- 记录跨域请求日志用于监控
- 定期审查允许的域名列表
通过以上方法,可以有效解决 PHP 应用中的跨域问题,确保前后端能够正常通信。
评论