跨域指的是浏览器出于安全考虑,默认不允许一个域名下的 JavaScript 脚本请求另一个域名下的资源,除非后者明确允许。本文将详细讲解跨域的原因、原理以及多种解决方案。
什么是跨域?
跨域问题源于浏览器的同源策略(Same-origin policy)。同源策略限制了来自不同源的“写”操作,但允许“读”操作。所谓同源,是指协议、域名和端口号三者都相同。例如,http://example.com:8080
和 http://example.com:8081
就是不同源的。
为什么会有同源策略?
同源策略是为了防止恶意网站在用户不知情的情况下向其他网站发起请求,从而窃取用户的敏感信息或执行恶意操作。例如,跨站请求伪造(CSRF)就是利用了跨域请求来执行未授权的操作。
跨域请求类型
跨域请求主要有两种类型:
- 简单请求(Simple Request):这类请求包括 GET、HEAD、POST 方法,并且 POST 请求的 Content-Type 限于
text/plain
、multipart/form-data
、application/x-www-form-urlencoded
。 - 预检请求(Preflight Request):对于非简单请求(如 PUT、DELETE、PATCH 方法,或 POST 请求的 Content-Type 不符合简单请求的要求),浏览器会先发送一个 OPTIONS 请求来询问服务器是否允许跨域请求。
解决跨域的方法
1. JSONP(JSON with Padding)
JSONP 是一种绕过同源策略的方法,适用于 GET 请求。原理是动态插入 <script>
标签来请求数据,并通过回调函数来处理返回的结果。
优点:实现简单,不需要后端做额外的工作。
缺点:只能用于 GET 请求,且安全性较低。
2. CORS(Cross-Origin Resource Sharing)
CORS 是一种更安全、更通用的跨域解决方案。它通过在 HTTP 响应头中添加特定的字段来允许跨域请求。
关键响应头:
- Access-Control-Allow-Origin:指定允许访问该资源的域名,可以是具体的域名(如
http://example.com
)或通配符*
表示允许所有域名。 - Access-Control-Allow-Methods:指定允许的请求方法(如 GET、POST)。
- Access-Control-Allow-Headers:指定允许的请求头字段。
- Access-Control-Allow-Credentials:允许携带 cookie 或认证信息,值为
true
或false
。
示例:
// Node.js 示例
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
if ('OPTIONS' == req.method) {
res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
return res.status(200).json({});
}
next();
});
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from server!' });
});
app.listen(3000, () => console.log('Listening on port 3000'));
3. 代理服务器
在开发环境中,可以使用代理服务器来转发请求,避免直接跨域请求。Vue CLI 和 Create React App 等脚手架工具都支持配置代理。
Vue CLI 示例:
在 vue.config.js
中配置代理:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://backend.example.com',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
4. 使用 Nginx 或 Apache 作为反向代理
在生产环境中,可以使用 Nginx 或 Apache 等反向代理服务器来处理跨域请求。
Nginx 配置示例:
location /api/ {
proxy_pass http://backend.example.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
5. 跨域资源共享配置
在某些情况下,如果后端无法直接修改响应头,可以通过配置 CORS 中间件或插件来实现。
示例:Express 中间件:
const cors = require('cors');
app.use(cors());