历史上出现过的跨域⼿段有很多,本章主要介绍目前主流的3种跨域⽅案:
- JSONP
- CORS
- 服务器代理(webpack代理, Nginx反向代理)
JSONP
这是一种非常经典的跨域方案,它利用了<script> 标签不受同源策略的限制的特性,实现跨域效果。
优点:
- 实现简单
- 兼容性好
缺点:
- 只支持 GET 请求 (因为
<script>标签只能发送 GET 请求) - 存在被 XSS 攻击的可能,缺乏安全性保证
- 需要服务端配合改造
实现示例:
// 1. JSONP 发送请求的函数封装
function JSONP({ url, params, callbackKey, callback }) {
// 在参数中指定 callback 名字
params = params || {} params[callbackKey] = 'jsonpCallback'
// 预留 callback
window.jsonpCallback = callback
// 拼接参数字符串
const queryString = Object.keys(params).map(key => `${key}=${params[key]}`).join('&')
// 创建 script 标签
const script = document.createElement('script')
script.setAttribute('src', `${url}?${queryString}`)
// 插⼊ DOM 树
document.body.appendChild(script)
}
// 2. 调用示例
JSONP({
url: 'http://s.weibo.com/ajax/jsonp/suggestion',
params: {
key: 'test'
},
callbackKey: '_cb',
callback(res) {
console.log(res.data)
}
})
CORS
跨域资源共享(CORS),这是⽬前比较主流的跨域解决⽅案,它利用一些额外的 HTTP 响应头来通知浏览器允许访问来自指定 origin 的非同源服务器上的资源。
当在⼀个资源中去请求与本资源所在的服务器有不同协议、域、或端⼝的另一个资源时,就会发起⼀个跨域 HTTP 请求。
如果你⽤的是 Node.js 的 Express 框架,则可以这样来进行设置:
// 创建一个 CORS 中间件
function allowCrossDomain(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://example.com');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
//...
app.configure(function() {
// ...
// 为 Express 配置 CORS 中间件
app.use(allowCrossDomain);
// ...
});
在实际项目中,建议使用一些已经比较成熟的开源中间件。
Nginx反向代理
这是目前最方便,最推荐使用的跨域解决方案。Nginx 本身是⼀款极其强⼤的 Web 服务器,轻量级、启动快、⾼并发。
现在的后端服务程序,通常会使用 Nginx 进行反向代理:
反向代理的原理其实很简单:
Nginx 作为代理服务器,所有客户端的请求都必须先经过 Nginx 的处理,然后再将请求转发给其他后端程序(比如 Node.js 或Java 程序),这样就规避同源策略的影响
下面是一个配置 Nginx 反向代理的示例配置文件:
# 进程, 可更具 cpu 数量调整
worker_processes 1;
events {
# 连接数
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#连接超时时间,服务器会在这个时间过后关闭连接。
keepalive_timeout 10;
# 开启 Gzip 压缩
gzip on;
# 直接请求nginx也是会报跨域错误的这⾥设置允许跨域
# 如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了)
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
# srever模块配置是http模块中的⼀个⼦模块,⽤来定义⼀个虚拟访问主机
server {
listen 80;
server_name localhost;
# 根路径指到index.html
location / {
root html;
index index.html index.htm;
}
# 请求转发:
# 例如 http://localhost/api 的请求会被转发到 http://192.168.0.103:8080
location /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_pass http://192.168.0.103:8080;
}
# 重定向错误⻚⾯到/50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}