跨域请求是指在浏览器中,使用 JavaScript 向不同域名的服务器发送 HTTP 请求。这在现代 Web 开发中非常常见,但由于安全原因,浏览器默认限制跨域请求。为了实现跨域访问,有几种常见的解决方案:
一、CORS(Cross-Origin Resource Sharing)
CORS 是一种现代的跨域解决方案,通过 HTTP 头来允许服务器声明哪些来源可以访问资源。浏览器会根据服务器返回的 CORS 头信息决定是否允许跨域请求。
优点:
- 支持多种 HTTP 方法(GET、POST、PUT、DELETE 等)。
- 安全性较高,能细粒度控制哪些来源可以访问资源。
缺点:
需要服务器端配置。
CORS 的工作流程如下:
- 浏览器向服务器发送 AJAX 请求,并在请求头中添加一个
Origin
字段,表示请求的来源。 - 服务器接收到请求后,检查
Origin
字段,判断请求是否来自允许的域名。如果请求来自允许的域名,则在响应头中添加一个Access-Control-Allow-Origin
字段,表示允许该域名访问资源。 - 浏览器接收到响应后,检查
Access-Control-Allow-Origin
字段,判断响应是否允许当前域名访问。如果允许访问,则浏览器将响应传递给 JavaScript 代码,否则抛出一个错误。
CORS 的优点是安全性高,可以避免跨站点脚本攻击(XSS)和跨站点请求伪造(CSRF)等安全问题。缺点是需要服务器支持,而且需要在响应头中添加一些额外的字段,增加了服务器的负担。
二、JSONP(JSON with Padding)
JSONP 是一种早期的跨域解决方案,主要用于 GET 请求。它利用 <script>
标签可以跨域加载资源的特性,通过动态创建 <script>
标签来实现跨域请求。
优点:
- 简单易用,不需要服务器端配置。
缺点:
- 只支持 GET 请求,无法使用 POST、PUT、DELETE 等其他 HTTP 方法。
- 容易遭受 XSS 攻击。
示例:
客户端:
<script>
function handleResponse(data) {
console.log(data);
}
var script = document.createElement('script');
script.src = 'https://example.com/api?callback=handleResponse';
document.body.appendChild(script);
</script>
服务器端:
<?php
$callback = $_GET['callback'];
$data = array('message' => 'Hello, World!');
echo $callback . '(' . json_encode($data) . ')';
?>
三、服务器端代理
服务器端代理是一种通过在同域名的服务器上设置一个代理,将跨域请求转发到目标服务器的解决方案。这样客户端只需要与同域名的服务器通信,避免了跨域问题。
优点:
- 可以完全避免跨域问题。
- 支持所有 HTTP 方法。
缺点:
- 需要额外的服务器配置和资源。
四、Nginx 反向代理
Nginx 反向代理是一种通过在 Nginx 服务器上配置反向代理,将跨域请求转发到目标服务器的解决方案。这样客户端只需要与同域名的 Nginx 服务器通信,避免了跨域问题。
优点:
- 可以完全避免跨域问题。
- 支持所有 HTTP 方法。
- Nginx 配置灵活,性能高。
缺点:
- 需要额外的服务器配置和资源。
server {
listen 80;
server_name yourdomain.com;
location /api/ {
proxy_pass https://example.com/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
常见跨域报错
如果设置多个Access-Control-Allow-Origin也会报错
Access to XMLHttpRequest at ‘https://upload.tapd.cn/index.php?action=upload’ from origin ‘https://www.tapd.cn’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: The ‘Access-Control-Allow-Origin’ header contains multiple values ‘*, https://www.tapd.cn’, but only one is allowed.