实现跨域

89 阅读3分钟

历史上出现过的跨域⼿段有很多,本章主要介绍目前主流的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 进行反向代理:

image.png

反向代理的原理其实很简单:

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; 
    } 
  } 
}

参考文章:跨域方式实现原理