跨域解决方法

一、概念

同源:域名、端口、协议均相同。
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的

二、跨域解决方法

  1. 跨域资源共享(CORS)
  2. jsonp
  3. nginx代理跨域
  4. webpack设置代理proxy/nodejs中间件代理跨域
  5. postMessage跨域
  6. WebSocket协议跨域
  7. document.domain + iframe跨域
  8. location.hash + iframe跨域
  9. window.name + iframe跨域

以上9种常见的跨域解决方案,
CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;
jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;
Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。
postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。
document.domain+iframe适合主域名相同,子域名不同的跨域请求。

1、跨域资源共享(CORS)

所有的跨域请求(简单或非简单)总会包含一个origin的请求头部,由浏览器添加不受用户控制。值由协议、域名、端口组成,说明请求的来源。下面为一个Origin头部示例:
Origin: http://0.0.0.0:8000
服务器接受这个请求,会在响应头Access-Control-Allow-Origin回发相同的源信息。( * 表明该资源可以被任意外域访问)
Access-Control-Allow-Origin:http://0.0.0.0:8000

2、jsonp

jsonp的原理就是利用

服务端返回如下(返回时即执行全局函数):

handleCallback({"status": true, "user": "admin"})
3、nginx代理跨域
  1. nginx配置解决iconfont跨域
  2. nginx反向代理接口跨域
4、 webpack设置代理proxy/nodejs中间件代理跨域
  1. 使用webpack搭建的项目,可以在webpack配置文件配置proxy代理
 // 接口代理示例
  proxy: {
    "/boss": {
      "target": "https://test-overseas.91dbq.com",
      "changeOrigin": true, //如果跨域,需要配置这个参数
      "secure":false, //如果是https请求,要配置这个参数
      "pathRewrite": { "^/boss" : "/boss-gateway-sg/boss" }
    }
  },
  1. 原生可以使用中间件代理
// 中间件服务器
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
    // 代理跨域目标接口
    target: 'https://test-overseas.91dbq.com',
    changeOrigin: true,

    // 修改响应头信息,实现跨域并允许带cookie
    onProxyRes: function(proxyRes, req, res) {
        res.header('Access-Control-Allow-Origin', 'http://0.0.0.0:8000');
        res.header('Access-Control-Allow-Credentials', 'true');
    },

    // 修改响应信息中的cookie域名
    cookieDomainRewrite: '0.0.0.0:8000'  // 可以为false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');
5、postMessage跨域
6、WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

user input:
7、document.domain + iframe跨域

此方案仅限主域相同,子域不同的跨域应用场景。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
1.)父窗口:http://www.domain.com/a.html



2.)子窗口: http://child.domain.com/b.html


8、location.hash + iframe跨域

实现原理: a与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现:A域:a.html -> B域:b.html -> A域:c.html,a与b不同域只能通过hash值单向通信,b与c也不同域也只能单向通信,但c与a同域,所以c可通过parent.parent访问a页面所有对象。

1.)a.html:http://www.domain1.com/a.html



2.)b.html:http://www.domain2.com/b.html



3.)c.html:http://www.domain1.com/c.html


9、window.name + iframe跨域

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
1.)b.html:http://www.domain2.com/b.html


2.)a.html:http://www.domain1.com/a.html

var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');
    // 加载跨域页面
    iframe.src = url;
    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    iframe.onload = function() {
        if (state === 1) {
            // 第2次onload(同域proxy页)成功后,读取同域window.name中数据
            callback(iframe.contentWindow.name);
            destoryFrame();
        } else if (state === 0) {
            // 第1次onload(跨域页)成功后,此时已拿到window.name,然后切换到同域代理页面
            iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
            state = 1;
        }
    };
    document.body.appendChild(iframe);

    // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
};

// 请求跨域b页面数据
proxy('http://www.domain2.com/b.html', function(data){
    alert(data); //This is domain2 data!
});

3.)proxy.html:http://www.domain1.com/proxy.html
中间代理页,与a.html同域,内容为空即可。

你可能感兴趣的:(跨域解决方法)