博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
跨域解决方案汇总
阅读量:6433 次
发布时间:2019-06-23

本文共 5379 字,大约阅读时间需要 17 分钟。

跨域解决方案汇总

同源策略

由于浏览器的同源策略,如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源,只要有一者不同,就会造成跨域

jsonp

jsonp由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定。而数据就是传入回调函数中的JSON数据。形式如下:

remote.js代码如下:

localHandler({"result":"我是远程js带来的数据"});

服务器根据请求里的callback=localHandle知道了本地的回调函数名称localHandle,查询字符串为name=value,然后服务器执行相对应的函数并返回数据给本地的localHandle回调函数。

建议动态的创建script来查询

jsonp优点

简单易用,能够直接访问响应文本,支持浏览器和服务器之间的双向通信

jsonp缺点

  • 安全性不足。借助JSONP有可能进行跨站请求伪造(CSRF)攻击,当一个恶意网站使用访问者的浏览器向服务器发送请求并进行数据变更时,被称为CSRF攻击。由于请求会携带cookie信息,服务器会认为是用户自己想要提交表单或者发送请求,而得到用户的一些隐私数据。
  • 错误原因不易找。JSONP缺乏错误处理机制,如果脚本注入成功后,就会调用回调函数,但是注入失败后,没有任何提示。这就意味着,当JSONP遇到404、505或者其他服务器错误时,你是无法检测出错原因的。我们能够做的也只有超时,没有收到响应,便认为请求失败,执行对应的错误回调。
  • 只能适用于get请求。只能使用GET请求就意味着很多限制,提交到服务器的数据量将受限于浏览器的最大URL长度。

jsonp封装

/** * JSONP请求工具 * @param url 请求的地址 * @param data 请求的参数 * @returns {Promise
} */const request = ({url, data}) => { return new Promise((resolve, reject) => { // 处理传参成xx=yy&aa=bb的形式 const handleData = (data) => { const keys = Object.keys(data) const keysLen = keys.length return keys.reduce((pre, cur, index) => { const value = data[cur] const flag = index !== keysLen - 1 ? '&' : '' return `${pre}${cur}=${value}${flag}` }, '') } // 动态创建script标签 const script = document.createElement('script') // 接口返回的数据获取 window.jsonpCb = (res) => { document.body.removeChild(script) delete window.jsonpCb resolve(res) } script.src = `${url}?${handleData(data)}&cb=jsonpCb` document.body.appendChild(script) })}// 使用方式request({ url: 'http://localhost:9871/api/jsonp', data: { // 传参 msg: 'helloJsonp' }}).then(res => { console.log(res)})

空iframe加form

const requestPost = ({url, data}) => {  // 首先创建一个用来发送数据的iframe.  const iframe = document.createElement('iframe')  iframe.name = 'iframePost'  iframe.style.display = 'none'  document.body.appendChild(iframe)  const form = document.createElement('form')  const node = document.createElement('input')  // 注册iframe的load事件处理程序,如果你需要在响应返回时执行一些操作的话.  iframe.addEventListener('load', function () {    console.log('post success')  })  form.action = url  // 在指定的iframe中执行form  form.target = iframe.name;   //target规定在何处打开 action URL。这里可以通过iframe.name来指定iframe  form.method = 'post'  for (let name in data) {    node.name = name    node.value = data[name].toString()    form.appendChild(node.cloneNode())  }  // 表单元素需要添加到主文档中.  form.style.display = 'none'  document.body.appendChild(form)  form.submit()  // 表单提交后,就可以删除这个表单,不影响下次的数据发送.  document.body.removeChild(form)}// 使用方式requestPost({  url: 'http://localhost:9871/api/iframePost',  data: {    msg: 'helloIframePost'  }})

document.domain + iframe

通过document.domain将两个页面的域名都设置成相同域名,也可以实现跨域。 不过这个方法只适用于不同子域的框架间的交互

// a.html
// b.html

location.hash

通过location.hash来跨域,原理是改变url的hash部分来进行双向通信,父页面向iframe子页面通信只需监听自身的url变化来发送信息,而子页面向父页面通信就麻烦一些,由于两个页面不在同一个域,IE和Chrome都不允许修改parent.location.hash的值,所以需要创建一个和父页面同域的中间页,中间页可利用parent.parent访问父页面的所有对象。

该方法的缺点是会造成不必要的浏览器历史记录,并且有些浏览器不支持onhashchange事件,数据直接暴露在url中,数据容量和类型都有限等。

// a.html
// b.html
// c.html

window.name + iframe

window.name + iframe,利用window.name在不同页面(甚至不同域)加载后依旧存在,并且支持大小达到了2MB。 步骤: 首先在a页面中创建iframe,将src指向外域保存数据到window.name,再将iframe的src指向和a页面同域的b代理页面,借此让a页面以iframe.contentWindow.name的形式成功读取数据。

//a.htmlvar 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(跨域页)成功后,切换到同域代理页面            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);});
//b.html

postMessage跨域

postMessage是HTML5新增的window属性

用法:postMessage(data,origin)方法接受两个参数

//a.html
//b.html

CORS

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程中,都是浏览器自动完成,对于开发者来说,CORS和同源的AJAX通信没有差别,浏览器一旦发现AJAX跨域,就会自动添加一些附加的头部信息,根据请求的不同还会多出一次附加的请求。

实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就实现了跨域

服务器实现CORS通信的关键是设置以下请求头

  1. Access-Control-Allow-Origin,
    该字段时必选的,用于设置允许响应资源的origin
Access-Control-Allow-Origin: *  //允许所有域Access-Control-Allow-Origin: 
允许指定的origin
  1. Access-Control-Allow-Credentials 该字段是可选的,用于设置是否允许发送Cookie,该字段的值只能设为布尔值 ==true== ,删除该字段即可不发送。如果需要发送Cookie,这里还需要前端的AJAX请求设置 ==withCredentials== 属性,此外,Access-Control-Allow-Origin不能设为星号,必须明确指定与请求网页一致的域名,并且,Cookie依旧遵循同源策略,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
var xhr = new XMLHttpRequest();xhr.withCredentials = true;
  1. Access-Control-Expose-Header
    该字段可选,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。

CORS与JSONP比较

CORS与JSONP的使用目的相同,但是比JSONP更强大。

JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

转载于:https://www.cnblogs.com/y-dt/p/10458176.html

你可能感兴趣的文章
NGUI v301 官方详解 Example 2 - Interaction
查看>>
Centos7配置JAVA_HOME
查看>>
003# ADempiere系统简介(二)
查看>>
利用 squid 反向代理提高网站性能
查看>>
协变&逆变
查看>>
glide 与 SubsamplingScaleImageView 结合使用
查看>>
前嗅ForeSpider教程:采集图片/视频/资源文件
查看>>
Linux下find参数-mtime n,-n,+n详解
查看>>
oracle数据库创建用户方法
查看>>
生成原始的依赖jar包运行的jar的执行命令
查看>>
zabbix agentd windows安装
查看>>
Hadoop基础入门学习笔记(基本概念)
查看>>
C++之RAII惯用法
查看>>
opencv学习02-播放视频,注意没有声音
查看>>
top命令 -b -n5 -d10
查看>>
LAMP的部署
查看>>
IntelliJ Community 14 使用简要 2015-3-17
查看>>
Hadoop tutorial - - windows开发环境搭建 2015-3-20
查看>>
深入理解Magento – 第七章 – 自定义Magento系统配置
查看>>
Xcode常用快捷键
查看>>