多种跨域方案

218 阅读3分钟

同源策略

说到跨域就离不开浏览器的同源策略

  • 协议
  • 域名
  • 端口号

只要有一个不同,就是跨域

非同源下

非同源下,三种行为会受到限制

  • Cookie、LocalStorage 和 IndexDB 无法读取。
  • DOM 无法获得
  • AJAX 请求不能发送

跨域最常用的方案

  • jsonp(只能发送get请求)
  • cors
  • postMessage
  • document.domain
  • window.name
  • location.hash
  • http-proxy
  • nginx
  • websocket

自己实现一个jsonp

jsonp实际上返回的是一个函数调用

function jsonp(url,data,callback){
    var scriptObj = document.createElement('script');
    var head = document.getElementsByTagName('head')[0];
    var fnName = 'jquery_'+new Date().getTime();
    var arr = []
    for(var key in data){
        console.log(key)
        arr.push(key+'='+data[key]);
    }
    data = arr.join('&');
    window[fnName] = function(json){
        callback(json);
    }
    scriptObj.src = url + '?' + data + '&'+callback+'='+fnName;
    head.appendChild(scriptObj);
}
jsonp('http://10.252.55.52:1337/user/socialInfo',{
    page:1,
    pageList:5,
    type:1
},function(data){
    console.log(data)
})

function jsonp({url,params,cb}){
    return new Promise((resolve,reject)=>{
        window[cb] = function(data){
            resolve(data)
        }
        params = {...params,cb}
        let arrs = [];
        for(let key in params){
            arrs.push(`${key}=${params[key]}`)
        }
        let script = document.createElement('script')
        script.src = `${url}?${arrs.join('&')}`
        document.body.appendChild(script)
    })
}
jsonp({
    url:'',
    params:{},
    cb:'show'
}).then(data=>{
    console.log(data)
})

cors

cors方式解决跨域问题,往往是需要在服务端设置一些请求头

我们可以通过 req.headers.origin 获取origin

下面是常用的一些请求头的设置

setHeader('Access-Control-Allow-Origin':origin) //允许哪个源可以访问我

setHeader('Access-Control-Allow-Headers':) //允许携带哪个头访问我

setHeader('Access-Control-Expose-Headers':) //允许返回的头

setHeader('Access-Control-Allow-Methods':) //响应标头指定响应访问所述资源到时允许的一种或多种方法

setHeader('Access-Control-Max-Age':) //用来指定本次预检请求的有效期,单位为秒

setHeader('Access-Control-Allow-Credentials':true)

postMessage

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

举例

假如有一个页面a.html 位于 www.test.com 在a.html 通过iframe嵌入b.html,b.html位于www.domain.com下

<iframe src="http://www.domain.com/b.html" frameborder="0" id="frame"></iframe>

如果a.hhtml想和iframe通讯的话,可以如下操作

let frame = doucument.getElementById('frame') //想和谁通讯,就先获取这个窗口,此时我们需要的iframe里的window

frame.contentWindow.postMessage('你好啊',http://www.domain.com) //发送消息,后面要写明是给哪个域发的

在b.html接收

wondiw.onmessage = function(e){
    console.log(e.data)
    e.source.send('我不好',e.origin) //再回复消息
}
或者
document.addEventListener('message',  function(e){
   // content..... 
   }
)

document.domain

利用document.domain 实现跨域:

前提条件:这两个域名必须属于同一个一级域名,而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域.

例如:

www.sojson.com 下指到sojson.com 是可以的。

icp.sojson.com 下指到 sojson.com 是可以的。

但是

www.sojson.com 下指到 www.baidu.com 是不行的。

sojson.com 指到 baidu.com 还是不行的

如果我们要在当前页面下,“www.sojson.com/shiro” 下上传图片到 "cdn.sojson.com/images/" 下去

需要在两个页面都写上

if(document.domain !='sojson.com'){
    document.domain = 'sojson.com';
}

window.name

对于这个方案我的理解是

有一个a.html 位于www.test.com下

有一个b.html 位于www.domain.com下

两个页面想通讯的话

我们需要在a.html页面引入一个iframe

<iframe src="http://www.domain.com/b.html" frameborder="0" id="frame" onload="load"></iframe>
let first = true //是否第一次加载
function load(){
    if(first){
        let iframe = document.getElementById('frame')
        iframe.src='http://www.test.com/b.html' //最后必须把b.html设置为和a.html同域名的,这样才能获取iframe.contentWindow.name,不然会受同源策略的影响
        first = false
    }else{
        console.log(iframe.contentWindow.name)
    }
}

iframe.contentWindow.name就可以获取到这个数据了

在b.html我们可以把想传递的数据放到 window.name上

window.name='你好啊'

location.hash

有一个a.html 位于www.test.com下

有一个c.html 位于www.domain.com下

现在a.html想访问c.html

a.html

<iframe src="http://www.domain.com/c.html#iloveyou"></iframe>

wondiw.onhashchange = function(e){
    
}
或者
document.addEventListener('hashchange',  function(e){
   // content..... 
   }
)

c.html

console.log(window.location.hash) //获取这个hash值
let iframe = document.createElement('iframe')
iframe.src='http://www.test.com/b.html#idontloveyou"'
document.body.appendChild(iframe)

b.html

window.parent.parent.location.hash = location.hash

websocket

websocket属于高级api,平常我们一般都使用socket.io去进行兼容

服务端需要先安装ws

服务端代码如下:

let WebSocket = require('ws')
let wss = new WebSocket.Server({port:3000})
wss.on('connection',function(ws){
    ws.on('message',function(data){ //服务端接收消息
        console.log(data)
        ws.send('我不好') //服务端发送消息
    })
})

客户端代码如下:

let socket = new WebSocket('ws://localhost:3000')
socket.onopen = function(){
    socket.send('你好啊')
}
socket.onmessage = function(e){
    console.log(e.data) // 客户端接收消息
}

nginx

首先安装nginx

如果是mac的话

brew install nginx //安装nginx

nginx.conf //配置文件

下面是一些常用的配置

add_header "Access-Control-Allow-origin" "*"